C++ to Rust Phrasebook

2025-05-3022:1822178cel.cs.brown.edu

This book is designed to help C++ programmers learn Rust. It provides translations of common C++ patterns into idiomatic Rust. Each pattern is described through concrete code examples along with high…

This book is designed to help C++ programmers learn Rust. It provides translations of common C++ patterns into idiomatic Rust. Each pattern is described through concrete code examples along with high-level discussion of engineering trade-offs.

The book can be read front-to-back, but it is designed to be used random-access. When you are writing Rust code and think, "I know how to do this in C++ but not Rust," then look for the corresponding chapter in this book.

This book was hand-written by expert C++ and Rust programmers at Brown University's Cognitive Engineering Lab. Our goal is provide accurate information with a tasteful degree of detail. No text in this book was written by AI.

If you would like updates on when we add new chapters to this book, you can drop your email here.

Other resources

If you have zero Rust experience, you might consider first reading The Rust Programming Language or getting a quick overview at Learn X in Y Minutes.

If you are primarily an embedded systems programmer using C or C++, this book is a complement to The Embedded Rust Book.

Compared to resources like the Rustonomicon and Learn Rust With Entirely Too Many Linked Lists, this book is less about "Rust behind the scenes" and more about explicitly describing how Rust works in terms of C++.

Feedback on this book

At the bottom of every page there is a link to a form where you can submit feedback: typos, factual errors, or any other issues you spot.

If you answer the quizzes at the end of each chapter, we will save your responses anonymously for research purposes.


Read the original article

Comments

  • By jpc0 2025-05-3110:264 reply

    There 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.

    • By pjmlp 2025-05-3112:241 reply

      As someone that has been around C and C++ communities since 1990's, I also expect that in the long term Rust won't be able to escape this phenomenon, even with editions.

      Like any other programming language that has made it into the top 10 over several decades of production code.

      The happy path will get fuzzier, as more humans have their own opinion on what means to write code on the ecosystem, with various kinds of backgrounds and their own agendas, companies whose IT refuses to upgrade, the amount of implementations in the industry increases, teachers not bothering to keep up to date,...

      • By leoh 2025-05-3117:091 reply

        Hasn't been the case the last 10y. If anything there has been convergence.

        • By eslaught 2025-05-3122:571 reply

          Async vs. non-async is the main example today. There are libraries that support one or the other, or sometimes one library will have two usage modes (effectively two different code bases) because you can't really mix them.

          In the future who knows, because we don't know what features will get added to the language.

          • By surajrmal 2025-06-012:251 reply

            std vs nostd is another big one. Within nostd there are a ton of tiny fragmented worlds. For example, the Linux kernel ecosystem will likely development its own flavor of rust, especially when it comes to memory model. Old Linux distributions will end up with fairly ancient compiler versions that require code to stick to older conventions. I doubt well end up with people stuck on targeting c89 sort of situations, but things may trend in that general direction.

            If you develop that for servers or mobile/desktop applications it might look more homegenous, but their are a lot of segments beyond those out there.

            • By leoh 2025-06-0221:28

              Yikes. May we fare more with more ease than C++.

    • By m-schuetz 2025-05-3113:222 reply

      Are C++ modules actually production ready now? Last I checked, they still weren't properly supported accross all major compilers.

      • By pjmlp 2025-05-3116:09

        Depends on which requirements one has.

        If staying only on VC++ or clang latest, with MSBuild or CMake/ninja, they kind of are, on my hobby coding I have been using modules for quite a while now, check the C++ projects on Github.

        Tip, for node native modules, which node-gyp probably will never support them, they are supported via cmake.js.

      • By psyclobe 2025-05-3118:11

        No…

    • By Toritori12 2025-05-3111:482 reply

      C++ 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

      • By IshKebab 2025-05-3112:181 reply

        Does Clangd support it? It's much better than Microsoft's C++ extension - and open source!

        • By jpc0 2025-05-3115:30

          Yes 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.

      • By pjmlp 2025-05-3112:261 reply

        Visual Studio and Clion are the ones currently with the best experience regarding modules.

        And in VS could be much better, but EDG has other priorities.

        VSCode isn't really that great option for C/C++.

        • By m-schuetz 2025-05-3113:194 reply

          I prefer vscode simply because VS is excruciatingly slow. e.g. the file open pane in vscode pretty much instantly lists the file I'm looking for, while the counterpart in VS (ctrl+,) takes several seconds and intermixes search results for files and file contents, when I'm only interested in files.

          • By tom_ 2025-05-3118:57

            DPack (free) has a useable file browser. I use Visual Assist (commercial) these days and that has one too. Both pop up pretty much instantly.

            Both available from the extension marketplace.

          • By Toritori12 2025-05-3115:52

            Same here, those "mini" delays are not worth.

          • By NotCamelCase 2025-05-3115:27

            > VS (ctrl+,) takes several seconds and intermixes search results for files and file contents, when I'm only interested in files.

            I hate this a lot. It's gotten so bad the last couple of releases to the point that I try and use VS Code more in lieue of VS except debugging.

            How they could let such fundamental functionality get broken is beyond me.

          • By pjmlp 2025-05-3116:031 reply

            Ctrl+, followed by f filename, you will get the file.

            The only reasons I use VSCode are the plugins I cannot get on VS, like Powershell, Rust, Azure tooling, and for stuff like Next.js, better use an editor that is anyway a browser in disguise.

            Performance has never been a part of my decision flowchart.

            • By m-schuetz 2025-05-3116:261 reply

              I know it gets you the file...after several seconds. Vscode gives it instantly. I use this feature so often, it's basically a dealbreaker for VS.

              I keep both editors open, but VS is basically just there to hit the compile button and for the occasional debugging.

              • By pjmlp 2025-05-3116:351 reply

                Dunno, time to check your plugins slowing down the IDE.

                Better not having Resharper around.

                • By m-schuetz 2025-05-3120:311 reply

                  I have zero plugins installed. VS is just so slow that it's even outmatched by a javascript/Electron app like vscode.

                  • By pjmlp 2025-06-017:051 reply

                    Then it is definitely a "my computer/your computer" problem.

                    • By m-schuetz 2025-06-017:40

                      My computer has a PCIe 5 SSD, Ryzen 7950 and 128GB Ram. This is solely a VS problem, as you can see from the other posters with the same issue. And from the fact that vscode displays results instantly. It's only ever VS that's slow, accross any system I've ever used it with.

    • By dwattttt 2025-06-011:501 reply

      > 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.

      This guide does exactly that in C++ for methods on enums.

      For members, won't this result in all instances of an "enum" having all fields? That's not really comparable to Rust enums, if that's what you mean.

      • By jpc0 2025-06-0515:17

        > For members, won't this result in all instances of an "enum" having all fields? That's not really comparable to Rust enums, if that's what you mean.

        Rust enums are not enums, they are tagged unions and there is a C++ equivalent, std::variant which was discussed. It’s not got nice tooling around it like match but it exists and can be used identically.

  • By EliRivers 2025-05-318:445 reply

    One 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?

    • By atq2119 2025-05-3112:145 reply

      To 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.

      • By Conscat 2025-05-3115:34

        > the object construction can be done with static methods

        I've done that a lot too, but I found that free functions are much better for this than static member functions, because you can't get CTAD from static member functions. For example, with constructors we could write:

          vector{1, 2, 3}; // deduces vector<int>
        
        And with a static member, we would need:

          vector<int>::init(1, 2, 3);
        
        With a free function, we could write:

          make_vector(1, 2, 3); // returns vector<int>

      • By legobmw99 2025-05-3112:51

        This 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

      • By TuxSH 2025-05-3113:141 reply

        Two-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.

        • By Conscat 2025-05-3115:41

          Nothing 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

      • By fooker 2025-05-3122:07

        Right, when construction is fallible you need a factory.

        Constructors are called in surprising places when running a C++ program. For example, think of a copy constructor failing somewhere far away from the code you are writing. If C++ allowed construction to fail, the control flow of propagating these errors would be tedious and invasive.

        Hence exceptions as the only way to fail in a construction.

      • By EliRivers 2025-05-3114:131 reply

        To 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.

        • By Mond_ 2025-05-3114:303 reply

          How would you deal with fallible construction of objects while avoiding exceptions in idiomatic C++?

          • By jandrewrogers 2025-05-3116:253 reply

            The standard idiom is to have a sentinel state for the object indicating it is invalid. For objects without trivial destructors or which may be read after being moved-from (a valid behavior in some systems code contexts) then you need a sentinel state anyway because moves in C++ are non-destructive.

            C++ uses deferred destruction as a standard tool to solve a variety of problems.

            • By jjmarr 2025-05-3118:281 reply

              > which may be read after being moved-from (a valid behaviour in some systems code contexts)

              std::move as applied to standard library types will leave the object in a "valid but unspecified state".[1] If you're leaving the object in an invalid state (one where the invariants are broken), you're not writing idiomatic C++.

              [1] https://en.cppreference.com/w/cpp/utility/move.html

              • By jandrewrogers 2025-05-3118:38

                I am using “invalid” here in the semantic sense of not containing a meaningful value. It is not invalid in a structural sense.

            • By atq2119 2025-05-3122:57

              This makes sense for objects that can enter an equivalent invalid state after successful construction as the result of a method call (e.g. a file or stream).

              For objects that don't have that property, you're just exchanging one kind of badness in the design for a different but ultimately equivalent badness.

            • By sunshowers 2025-05-3117:002 reply

              So the existence of an object of the type does not act as a static proof that the state is valid?

              • By jandrewrogers 2025-05-3118:341 reply

                This is correct (and I am using “invalid” here in a semantic sense, it is still structurally valid). There are a number contexts in low-level systems code where a static proof is not possible even in theory, so there needs to be a way for code to inspect object validity at runtime. Process address space isn’t entirely private, external actors that your process doesn’t entirely control can modify it e.g. via DMA.

                The C++ compiler largely assumes that such static proof is possible by default and has no way of knowing if it is not. To address this, the C++ language has added features for annotating objects to indicate that static proofs of state are not possible at compile-time (e.g. std::launder).

                Database kernels are the most extreme example of this because most objects in the address space don’t own their memory address and the mechanism that temporarily puts an object at a particular memory address is not visible at compile-time. Consequently, object location and state has to be resolved dynamically at runtime.

                • By sunshowers 2025-05-3119:18

                  Definitely agree that there's plenty of cases in systems code where static proofs are impossible. That makes it all the worse when you give up on static proofs in places where they are possible.

              • By steveklabnik 2025-05-3117:29

                That’s correct, and “valid but unspecified state” is possible/common too.

          • By metaltyphoon 2025-05-3114:551 reply

            This was one of the best decisions that Rust and Go did; not have constructors. In C# this is super annoying too, specially when you need an async operation to construct a type. This is usually done by having an private constructor and then using a static public method to create the type.

            • By Conscat 2025-05-3115:361 reply

              Rust and Go have no form of a conversion operator (even if not a constructor), which makes scripting a type system essentially impossible. Numeric libraries in both of those languages are extremely cumbersome, largely for this reason.

              • By tialaramex 2025-06-0112:511 reply

                I don't understand this comment at all.

                Rust not only has the 'as' operator for this exact purpose, but it also has the suite of traits From, Into, TryFrom and TryInto for the infallible and fallible conversions respectively.

                • By Conscat 2025-06-021:38

                  As is an infix operator, it can never be invoked implicitly to draw these relationships between different types.

          • By pjmlp 2025-05-3116:062 reply

            Idiomatic C++ uses exceptions.

            The standard doesn't allow to disable language features.

            Anyone that goes into the dark side of disabling language features is writing unidiomatic C++ with compiler specific extensions.

            • By sunshowers 2025-05-3117:081 reply

              Can you think of good reasons why an organization would hesitate to use C++ exceptions?

              • By pjmlp 2025-06-017:021 reply

                Legacy code writen as if it was C with a C++ compiler, or that predates the C++98 standard (during the 1980-90's, where C++ARM was the only guidance), the Orthodox C++ folks, claiming that they are too slow or bloated (most of the time based on hearsay and not profiled), on embedded computers better than everything I owned since 1980's until 2000's, put together.

                The same folks won't have a second thought distributing statically linked binaries that triple the size, while using languages that don't do exceptions, but then it isn't bloat, talk about being coherent.

                • By sunshowers 2025-06-0117:58

                  I think another reason is that C++ mutexes typically don't poison on throwing an exception.

            • By atq2119 2025-05-3123:001 reply

              That's a bold statement, considering that many of the largest C++ code bases - including at least one of the few remaining C++ compilers! - don't use exceptions.

              • By pjmlp 2025-06-016:51

                I love bold statements, and if you mention either LLVM or Chrome, it isn't as if the Google's C++ style guide is any piece of art.

                Which anyone that bothers to make such claims, should be aware what it actually says regarding exceptions.

                "On their face, the benefits of using exceptions outweigh the costs, especially in new projects. However, for existing code, the introduction of exceptions has implications on all dependent code. If exceptions can be propagated beyond a new project, it also becomes problematic to integrate the new project into existing exception-free code. Because most existing C++ code at Google is not prepared to deal with exceptions, it is comparatively difficult to adopt new code that generates exceptions."

                They don't use exceptions, because already started on the wrong foot, and like legacy code of Titanic size there is no turning around now.

                What does the Bible of idiomatic C++ says, aka C++ Core Guidelines?

                It has several advices on E section, regarding exception coding best practices.

    • By hardwaregeek 2025-05-3115:32

      The 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.

    • By pjmlp 2025-05-3112:29

      I 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.

    • By acje 2025-05-319:12

      A 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.

    • By pornel 2025-05-3114:521 reply

      The 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.

      • By trealira 2025-05-3117:47

        >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.

  • By amelius 2025-05-3113:022 reply

    I noticed that for most of the examples the Rust version is more verbose.

    • By pornel 2025-05-3115:061 reply

      C++ 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++.

      • By amelius 2025-05-3115:181 reply

        So you are saying that this approach teaches C++ users to use the wrong idioms in Rust?

        • By pornel 2025-05-3115:492 reply

          Wrong 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.

          • By mrlongroots 2025-05-3120:281 reply

            Which, 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".

            • By tialaramex 2025-06-0113:23

              One habit worth trying to adopt early in this mode is running clippy, Rust's linter. Typically invoked as `cargo clippy`

              Clippy likes idiomatic Rust and will suggest you change code that's not idiomatic into code which is, even when the machine code would be completely identical - the rationale being that the maintainer (later you with more Rust knowledge, a colleague, or even some stranger) is more likely to follow the idiomatic Rust and the whole point of source code is that it's for humans not machines.

              Clippy is no substitute for a capable human reviewer, it has no sense of taste or style, no higher level understanding of the problem, but it's free and it's right there and unlike a human reviewer you won't feel judged which can be sensitive when you're learning a new language and are used to having mastery.

          • By amelius 2025-05-3116:12

            Ok, do you have any examples to convince me?

    • By modulus1 2025-05-3118:321 reply

      And they even made the C++ version more verbose than it should have been. Most people would write:

      class Person { int age = 0; };

      I wish rust would make default struct field values this easy to write.

HackerNews