6 ms·
C++ to Rust Phrasebook
- leoh 1y agoEpic. This is really good.
- EliRivers 1y agoOne of the common pitfalls I've seen in my time is someone writing a language they are familiar with in a language that just doesn't fit; trying to apply idioms that flow well with one language to another language where that's just not a good way to achieve the same ends. An example I've seen a lot is a C thinker writing C++ classes with an init() function; sure, it works, but the C++ way is to do that in constructors. (All those about to start listing exceptions to that C++ idiom, please save it to the end, thanks!) The C thinker is still thinking about objects as "allocate memory, then set values" rather than the C++ way where allocation and initialisation are wrapped together into a single action (from the programmer's point of view). So what are these pitfalls for a C++ thinker when writing Rust? This "phrasebook" is embracing the idea of taking a C++ way of thinking and applying it to Rust, which I'm sure will be fine for many situations, but what are the C++ phrases that are just the wrong way to do things in Rust?
- acje 1y agoA resource like this is a good place to discuss where the two languages are near and far. Of course there are going to be styles within each language that differ as much as the languages themselves.
- atq2119 1y agoTo be fair, there's a reason for the pattern with init methods you're describing. C++ constructors can't return values. If construction is fallible, the only way to communicate the error is via C++ exceptions. If you're in a code base that embraces exceptions, that's fine. But (C++) exceptions kind of suck, so many code bases don't, and then you have to find some alternatives. Over the years, I've increasingly adopted a pattern where the constructor is private in this case and the object construction can be done with static methods - which is a bit more like Rust, actually.
- legobmw99 1y agoThis is fine if the method you’re talking about is static — as you point out, it’s really all Rust has — but is absolutely a design mistake if it is not, which I think is what the poster above is referring to. It’s a common anti pattern and means you have an object that is at-best useless and at-worst completely broken after you call the constructor but before you call some member function on it
- TuxSH 1y agoTwo-phase initialization also has the added benefit of usually making the object have a constexpr constructor (usually a default constructor) and therefore making it eligible for constinit. That said, construct_at also exists.
- Conscat 1y agoNothing prevents std::vector from having a `constexpr` default-constructor except that it's not considered useful to do if you cannot follow that up with initializing its data in a constant context. For instance, this isn't very useful: constinit vector<int> v; But this would be more so: constinit vector<int> v(16, 1); // Fill with 16 1's. And the reason we can't do this wouldn't be solved by splitting it into multiple functions. EDIT: Actually, come to think of it, C++20's vector already supports the first example. It's just not used much that way because it's not very helpful. https://godbolt.org/z/avY4M9oMK https://godbolt.org/z/avY4M9oMK
- EliRivers 1y agoTo be fair, there's a reason for the pattern with init methods you're describing. Without prejudice on any other reasons, the most common reason for this pattern I've seen is people thinking in languages that basically don't have constructors, yet writing C++. It's not a good reason.
- Mond_ 1y agoHow would you deal with fallible construction of objects while avoiding exceptions in idiomatic C++?
- pjmlp 1y agoI hate with passion two phase initialisation, C++ libraries that are bare bones C libraries wrapped in an extern "C" { }, malloc()/free(), C style coding and such.
- pornel 1y agoThe worst pitfall is Rust references == pointers. They are implemented as pointers, but their role is to give temporary (often exclusive) access that is restricted to a statically know scope, which is pretty specific and fits only some uses of some pointers/C++ references. In C++ pointers typically mean avoiding copying, but Rust references avoid storing/keeping the data. When these goals don't overlap, people get stuck with a dreadful "does not live long enough" whack-a-mole.
- trealira 1y ago>their role is to give temporary (often exclusive) access that is restricted to a statically know scope, which is pretty specific and fits only some uses of some pointers/C++ references You could have a vector of references to heap allocated data, as long as the references were parametrized by the same lifetime. You might do this if implementing a tree iterator using a vector as a stack, for instance. That goes beyond a statically known scope. But implementing a mutable iterator the same way would require a stack of mutable pointers (and therefore unsafe code whenever you dereference them), since mutable references have to be unique. That does seem like a bad limitation.
- hardwaregeek 1y agoThe rationale is probably that it’s better for C++ devs to write non idiomatic Rust than to keep writing unsafe C++. Like unless they use unsafe and completely circumvent the borrow checker, it’s still gonna be safer. Not letting perfect be the enemy of good and all. Plus idiomatic rust isn’t that strict a definition. Clippy will guide you for most of the simple stuff and the rest isn’t always worth following. Like people who try to do stuff “correctly” with traits often end up with way more complexity than it’s worth.
- npalli 1y agoInverting the original intent, this is great, learn how Rust improves certain things and use that to write better C++ if possible.
- tialaramex 1y agoPlenty of C++ practitioners who still intend to mostly write C++ after learning some Rust said it actually gave them useful insights they'll be applying in future C++ We can also see in the committee proposal papers "Rust does X" has for years now been a good comeback when you need to show that X is a realistic choice not just for languages like Python which may be less concerned about performance and incur a heavy runtime, a garbage collector, etc., but also a "real" language like C++. The paper which landed code.contains("FOO") in the C++ string handling code is an example, there's a long list of languages which do this but they made sure to mention Rust.
- 90s_dev 1y agoI will be using this to learn both Rust and C++ and see which one I like better.
- Tyr42 1y agoI've taken a look, and it's definitely expecting you to know some C++. Or at least, it spends equal time on both, which means it can't warn you about the foot guns in c++.
- skrishnamurthi 1y agoIt's definitely written from the perspective of someone who "knows C++". But I put that in quotes because there are (at least) two interpretations of that phrase. There's the person who doesn't know any C++ at all, and for that person, this is useless. But there's the person who knows a baseline of C++ but doesn't know modern C++, and they can use this as a way to modernize their C++ code while ignoring the Rust bits. But either way, yes, it doesn't warn you about C++ footguns (other than pointing you towards Rust <-;).
- jpc0 1y agoThere are patterns diffused in this paper that are modern C++ only in the sense that anything post C++11 is modern C++. That was 14 years ago, you will be hard pressed to find a toolchain that doesn’t support C++17 at this point, yes there is probably some unfortunate person building for debian old-stable or some ancient but still supported redhat but at that point you know you aren’t following modern practices and you have your reasons.
- pjmlp 1y agoI will also debate that plenty of "modern" C++ features that people attribute to C++11, were already possible throughout C++ARM to C++03, but apparently many either weren't paying attention, or only renaming their C files into .cpp/.cxx/.C. Just like the low level stuff done by MFC, and how much more ergonomic CSet++, OWL and VCL happened to be.
- jpc0 1y agoThere are so many different flavours of C++ put there that this guide doesn’t exactly do itself the credit it deserves. There are easy ways to implement stuff like enums with members in C++, just put an anonymous enum inside a class/struct, its possible but marked as not possible. Likewise when discussing modules in rust while completely ignoring the existence of modules in C++ that is actually support by modern tooling. There are other places where there are similar issues I have with the text (you can just add a compiler flag and get the same behaviour. Don’t even try and argue “but rust just does it out of the box”, I would need to rewrite my entire codebase vs just adding a few bits of text to my build system, these things are not equivalent) They didn’t discuss much about FFI at all, generally sayings “theres a crate for that and if there isn’t let us know”, in my experience the crates are not amazing for esoteric things (anything graphics’s related, ffmpeg is another one) and are actually significantly more painful to use that just writing in a restricted version of C++. Rust has a happy path, and they are broadening it incrementally, but theres is a lifetime of history before rust even existed in C++ that isn’t that easy to sweep under the rug.
- Toritori12 1y agoC++ modules still not supported on VSCode, always pissed when I get notifications from this 5 y.o. thread [0]. 0: https://github.com/microsoft/vscode-cpptools/issues/6302 https://github.com/microsoft/vscode-cpptools/issues/6302
- IshKebab 1y agoDoes Clangd support it? It's much better than Microsoft's C++ extension - and open source!
- jpc0 1y agoYes this. Clangd-19 which is current stable has very good support for modules. The only issues I’ve encountered is with import std; which quite honestly is bleeding edge. Your tooling (clangd+cmake) has to be pretty modern t those two are also the easiest to just upgrade since it’s dev time only. And obviously if it’s a discussion you have a C++20 compatible compiler. I’m happily using modules with gcc-14, clangd 19 and cmake 3.28 other than clangd 19 those are just packages you can install in Ubuntu 24.04 which is over a year old at this point.
- amelius 1y agoI noticed that for most of the examples the Rust version is more verbose.
- pornel 1y agoC++ syntax and semantics are optimized for C++ idioms, and Rust's aren't. Rust is more related to ML-family languages than C-family and OOP, so it needs to "emulate" some C++ idioms. This goes both ways, e.g. equivalent of Rust's pattern matching on enums with data translates to verbose and clunky C++.
- amelius 1y agoSo you are saying that this approach teaches C++ users to use the wrong idioms in Rust?
- pornel 1y agoWrong is too strong. The code is okay given the constraint — this is a guide for C++ programmers thinking in C++ terms, not for teaching purely idiomatic Rust from the ground up.
- amelius 1y agoOk, do you have any examples to convince me?
- mrlongroots 1y agoWhich, as a cpp programmer trying to pick up Rust, is honestly fine to begin with. Once you've written varying amounts of code in 5-10 programming languages, it is incredibly tedious to flip through pages trying to teach you how if conditions work and how for loops work: my brain doesn't pay attention even if I try. This is more like: how to survive rustc as a cpp programmer which is honestly your mindframe when you start out, and it sets you up for "okay now that you speak the syntax, this is how to really think in rust terms".
- deleted 1y ago[deleted]
- Animats 1y agoThe discussion of traits vs. classes glosses over a major difference - Rust traits have no associated data, and you cannot access the data of a parent trait. Trying to do object-oriented programming in Rust quickly leads to a mess. This is a huge problem if you try to write C++ in Rust. There's no mention of ownership at all. > Many libraries in Rust will offer two versions of an API, one which returns a Result or Option type and one of which panics, so that the interpretation of the error (expected exceptional case or programmer bug) can be chosen by the caller. Huh? Usually, in Rust you just put .unwrap() or .expect() on a function call that returns a result if you want a panic. More generally, most of the differences between Rust and C++ relate not on how to write imperative code, but how to lay out connections between data structures. Most hard problems in Rust relate to ownership design. That's true in C++, too, but you don't discover them until the program crashes at run time.
- unvalley 1y agoNice, it also helps me like a Rust developer that has no C++ experience.
- gtani 1y agobeen awhile sincei looked but i thought Drysdale's Effective Rust was a truly excellent book and aimed at simlar audience https://www.lurklurk.org/effective-rust/ https://www.lurklurk.org/effective-rust/