https://www.collabora.com/news-and-blog/blog/2025/05/06/matt-godbolt-sold-me-on-rust-by-showing-me-c-plus-plus/ Collabora Logo - Click/tap to navigate to the Collabora website homepage * About + Who we are + Our expertise + Our work + Open Source + Our ecosystem * Services + Guide + Train + Build + Integrate + Optimize + Maintain * Industries + Automotive + Digital TV + Silicon + OEM + VR/AR * News & Blog * Careers * Contact Menu button * About * Services * Industries * News & Blog * Careers * Contact * +44 (0)1223 362967 * +1 514 667 2499 * contact@collabora.com We're hiring! [heroSizer_] * * Home * News & Blog * Blog Matt Godbolt sold me on Rust (by showing me C++) Gustavo Noronha avatar Gustavo Noronha May 06, 2025 Share this post: [socialShar] [socialShar] [socialShar] [socialShar] [socialShar] Reading time: Matt Godbolt, of Compiler Explorer fame, is awesome and you should scour the web for every single bit of content he puts out. That is exactly what I was doing when I watched Correct by Construction: APIs That Are Easy to Use and Hard to Misuse. After 20+ years of working with C/C++, this theme resonates a lot with me. While watching the talk I kept thinking "Yes! And that's why Rust does it that way." I came out at the end thinking that this talk was actually a great way of getting the intuition for how Rust helps you beyond the whole memory safety thing, and that is what this article intends to show. But before we talk about that we should talk about the problems Matt raised and how he proposes to solve them in C++. Do yourself a favor and watch the full talk, but let me break one of them down! What's in a type Matt starts the talk by showing what a function that sends orders to a stock exchange might look like. void sendOrder(const char *symbol, bool buy, int quantity, double price) Before we go any further, let me just say he acknowledges floating point is not right for price and later talks about how he usually deals with it. But it makes for a nice example, bear with us. Another obvious improvement that anyone used to this stuff will call out immediately is the bool for identifying a buy. That is error prone and Matt does call it out towards the end of this section. But he first focuses on quantity and price and how C++ makes it really hard for you to stop callers from confusing them: the compiler will allow 1000.00 for quantity and 100 for price with no warnings, despite them being different types. It just silently converts. How about some type aliasing? #include using Price = double; using Quantity = int; void sendOrder(const char *symbol, bool buy, int quantity, double price) { std::cout << symbol << " " << buy << " " << quantity << " " << price << std::endl; } int main(void) { sendOrder("GOOG", false, Quantity(100), Price(1000.00)); // Correct sendOrder("GOOG", false, Price(1000.00), Quantity(100)); // Wrong } No luck! Both clang 19 and gcc 14 will take that and not complain - even with -std=c++23 -Wall -Wextra -Wpedantic, which I use for all of the C++ code in this article! A few rounds of improvements and we have the following version: #include class Price { public: explicit Price(double price) : m_price(price) {}; double m_price; }; class Quantity { public: explicit Quantity(unsigned int quantity) : m_quantity(quantity) {}; unsigned int m_quantity; }; void sendOrder(const char *symbol, bool buy, Quantity quantity, Price price) { std::cout << symbol << " " << buy << " " << quantity.m_quantity << " " << price.m_price << std::endl; } int main(void) { sendOrder("GOOG", false, Quantity(100), Price(1000.00)); // Correct sendOrder("GOOG", false, Quantity(-100), Price(1000.00)); // Wrong } We have classes, we have explicit constructors (very important or C++ will trip you!), we have unsigned types... and now it's hard to put a Price where you want a Quantity! But we can still give Quantity a negative value without a single compiler warning, even though we moved to an unsigned type. A bit more magic and we can make that go away: #include #include class Price { public: explicit Price(double price) : m_price(price) {}; double m_price; }; class Quantity { public: template explicit Quantity(T quantity) : m_quantity(quantity) { static_assert(std::is_unsigned(), "Please use only unsigned types"); } unsigned int m_quantity; }; void sendOrder(const char *symbol, bool buy, Quantity quantity, Price price) { std::cout << symbol << " " << buy << " " << quantity.m_quantity << " " << price.m_price << std::endl; } int main(void) { sendOrder("GOOG", false, Quantity(100u), Price(1000.00)); // Correct sendOrder("GOOG", false, Quantity(-100), Price(1000.00)); // Wrong } Finally we can get clang (and gcc) to complain loudly that this is being misused. All it took was a templated constructor that performs a static assert at compile time. Nice! order/order-5.cpp:13:19: error: static assertion failed due to requirement 'std::is_unsigned()': Please use only unsigned types 13 | static_assert(std::is_unsigned(), "Please use only unsigned types"); | ^~~~~~~~~~~~~~~~~~~~~ order/order-5.cpp:26:28: note: in instantiation of function template specialization 'Quantity::Quantity' requested here 26 | sendOrder("GOOG", false, Quantity(-100), Price(1000.00)); // Wrong | ^ 1 error generated. It's a lot of code, but at least we now have compiler protection. We are good, there is no other way to misuse quantity and price. Right? What if we need to pass in a value the user typed on a UI, such that we need to convert it from string? Well, you are out of luck again: sendOrder("GOOG", false, Quantity(static_cast(atoi("-100"))), Price(1000.00)); // Wrong Not only will it not fail to compile, but it will also not produce any errors at runtime. You will end up selling 4294967196 shares and going bankrupt. Not ideal. Matt keeps going, showing a few more magic tricks (and their pitfalls) to perform the runtime checks you will need to fully defend against this. I think this is a good point for us to stop on the C++ side and look at how Rust does, shall we? Enter Rust So is Rust any better? Rust has the benefit of decades of learning from all of these issues. And learn it did. Let's take a look. How does our first attempt fare? fn send_order(symbol: &str, buy: bool, quantity: i64, price: f64) { println!("{symbol} {buy} {quantity} {price}"); } fn main() { send_order("GOOG", false, 100, 1000.00); // Correct send_order("GOOG", false, 1000.00, 100); // Wrong } After all the work we had to do with C++ it can't be this easy, can it? error[E0308]: arguments to this function are incorrect --> order/order-1.rs:7:5 | 7 | send_order("GOOG", false, 1000.00, 100); // Wrong | ^^^^^^^^^^ ------- --- expected `f64`, found `{integer}` | | | expected `i64`, found `{float}` | note: function defined here --> order/order-1.rs:1:4 | 1 | fn send_order(symbol: &str, buy: bool, quantity: i64, price: f64) { | ^^^^^^^^^^ ------------ --------- ------------- ---------- help: swap these arguments | 7 | send_order("GOOG", false, 100, 1000.00); // Wrong | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Welp, what do you know? It even tells us the arguments we swapped, and everything. It's like we live in the future! Ok, but numbers are still easy to confuse, we could also create types like we did in C++ to make it extra explicit, right? Indeed, and let's throw in protection against negative values by switching from i64 to u64. Is it that easy? struct Price(pub f64); struct Quantity(pub u64); fn send_order(symbol: &str, buy: bool, quantity: Quantity, price: Price) { println!("{symbol} {buy} {} {}", quantity.0, price.0); } fn main() { send_order("GOOG", false, Quantity(100), Price(1000.00)); // Correct send_order("GOOG", false, Quantity(-100), Price(1000.00)); // Wrong } Yes, it is that easy: error[E0600]: cannot apply unary operator `-` to type `u64` --> order/order-4.rs:10:40 | 10 | send_order("GOOG", false, Quantity(-100), Price(1000.00)); // Wrong | ^^^^ cannot apply unary operator `-` | = note: unsigned values cannot be negated Ok, all that remains is the case where we need to convert the number from a string the user typed. I got you now, Rust... runtime input is not something you can fix at compile time, how would you ever be able to improve on the C++ case? struct Price(pub f64); struct Quantity(pub u64); fn send_order(symbol: &str, buy: bool, quantity: Quantity, price: Price) { println!("{symbol} {buy} {} {}", quantity.0, price.0); } fn main() { send_order("GOOG", false, Quantity(100), Price(1000.00)); // Correct send_order( "GOOG", false, Quantity("-100".parse::()), Price(1000.00), ); // Wrong } How about forcing the user to handle potential bad conversions caused by the target type not being able to represent the number we have on the string, does that help? error[E0308]: mismatched types --> order/order-6.rs:13:18 | 13 | Quantity("-100".parse::()), | -------- ^^^^^^^^^^^^^^^^^^^^^ expected `u64`, found `Result` | | | arguments to this struct are incorrect | = note: expected type `u64` found enum `Result` note: tuple struct defined here --> order/order-6.rs:2:8 | 2 | struct Quantity(pub u64); | ^^^^^^^^ help: consider using `Result::expect` to unwrap the `Result` value, panicking if the value is a `Result::Err` | 13 | Quantity("-100".parse::().expect("REASON")), | +++++++++++++++++ error: aborting due to 1 previous error Yes, it does help, I can't just blindly cast. Damn, you're good. This error should be enough for me as a user of this API to understand that I should elegantly handle this possibility and maybe return an error all the way to the UI saying "no negative numbers, please". But what if I just go ahead and add that .expect() it is telling me about and then a user types a negative number? Well, then I get a crash at runtime. Better than going bankrupt? I would say so. > ./order-6 GOOG false 100 1000 thread 'main' panicked at order/order-6.rs:16:18: Quantities cannot be negative: ParseIntError { kind: InvalidDigit } note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace Closing thoughts You know what's the most interesting part of this whole article? The thing Rust is very famous for, memory safety, did not feature at all. Sure, you could make the argument that mixing integers with floats is a memory issue, but you'd be stretching the definition most people have for memory safety. What we learn in this exercise is that a well designed language can protect you from mistakes in ways that go way beyond stopping you from writing a use after free or a data race. The design can save you a lot of brain cycles by not forcing you to think about how to protect your code from the simplest mistakes -- the language has your back. Now, let's be honest. Most of the brain power you save will go, as a Rust beginner, to figuring out how to convince the borrow checker what you are doing is correct. It gets better, I promise. But I won't lie to you: your first few months with the borrow checker will suck. Are we done here? Not really. There are two more topics that Matt covers in his talk that I will discuss in future articles, one of them related to my biggest pet peeve with C++. And for those of you who are thinking "if C++ had decades to learn it would also have features that are this well designed", well... let's just say you are in for a treat. Again, go check Matt Godbolt. Listen to what the man says, read everything he writes, watch every video of his talks on YouTube, and play with his Compiler Explorer. You'll learn a lot and have fun while doing it! Related Posts Persian Rug, Part 4 - The limitations of proxies Persian Rug, Part 4 - The limitations of proxies Persian Rug, Part 3 - The warp and the weft Persian Rug, Part 3 - The warp and the weft Persian Rug, Part 2 - Other ways to make object soups in Rust Persian Rug, Part 2 - Other ways to make object soups in Rust Related Posts Persian Rug, Part 4 - The limitations of proxies Persian Rug, Part 4 - The limitations of proxies Persian Rug, Part 3 - The warp and the weft Persian Rug, Part 3 - The warp and the weft Persian Rug, Part 2 - Other ways to make object soups in Rust Persian Rug, Part 2 - Other ways to make object soups in Rust Comments (6) 1. Zeeshan: May 06, 2025 at 10:12 PM Nice one, Gustavo, > a great way of getting the intuition for how Rust helps you beyond the whole memory safety thing, and that is what this article intends to show. This is the best part of. your post IMO. I'm so tired of people assuming that's the only thing Rust gives them. Reply to this comment Reply to this comment 2. Petko: May 06, 2025 at 10:21 PM Great article! I've personally caused similar bugs by swapping arguments in C++. But what I swapped were the float and bool arguments. Turns out it implicitly converts between float and bool without any warnings. I wasn't able to find a flag in msvc that prevents this from happening, so I just have to "be more careful"... Reply to this comment Reply to this comment 3. Andre: May 07, 2025 at 03:57 AM The article lacks compelling arguments. C# offers superior readability and error handling. While Rust's performance rivals that of C, its key advantage--memory management--is not adequately highlighted. Furthermore, the article omits the significant addition of "concepts" in C++20. Reply to this comment Reply to this comment 4. Nils Brunggel: May 07, 2025 at 05:04 AM Awesome but what about if you have two args of the same type that should not be interchanged like: readRegion(int x, int y, int width, int height) I guess you wound not create an XCoord struct or would you do that? Reply to this comment Reply to this comment 5. Raj: May 07, 2025 at 11:58 AM Interesting examples, this is similar where, if we define two functions one with int and another with double and call a function with integer then c++ compiler gives error due to ambiguity. Reply to this comment Reply to this comment 6. Stuart D Maclean: May 07, 2025 at 05:18 PM I had a couple of c++ books by Scott Meyers back in the early 90s. They showed how even then you could use aspects of the language to improve apis and reduce misuse. So while I enjoyed this article, the topic is hardly revolutionary. Reply to this comment Reply to this comment Add a Comment Name: [ ] Email: [ ] Website: [ ] [ ] Notify of New Replies: [ ] Please tick this box to confirm you have read and accept the terms of our privacy notice regarding collection/storage and usage of your personal data: * Allowed tags:
Add a new comment: [ ] [ ] [ ] [ ] [ ] Post Search the newsroom Search [Search news & blog..] [Search] Latest Blog Posts Matt Godbolt sold me on Rust (by showing me C++) 06/05/2025 Gustavo Noronha helps break down C++ and shows how that knowledge can open up new possibilities with Rust. Customizing WirePlumber's configuration for embedded systems 29/04/2025 Configuring WirePlumber on embedded Linux systems can be somewhat confusing. We take a moment to demystify this process for a particular... Evolving hardware, evolving demo: Collabora's Embedded World Board Farm 24/04/2025 Collabora's Board Farm demo, showcasing our recent hardware enablement and continuous integration efforts, has undergone serious development... Implementing Bluetooth on embedded Linux: Open source BlueZ vs proprietary stacks 27/02/2025 If you are considering deploying BlueZ on your embedded Linux device, the benefits in terms of flexibility, community support, and long-term... The state of GFX virtualization using virglrenderer 15/01/2025 With VirGL, Venus, and vDRM, virglrenderer offers three different approaches to obtain access to accelerated GFX in a virtual machine. Here... Faster inference: torch.compile vs TensorRT 19/12/2024 In the world of deep learning optimization, two powerful tools stand out: torch.compile, PyTorch's just-in-time (JIT) compiler, and NVIDIA's... About Collabora Whether writing a line of code or shaping a longer-term strategic software development plan, we'll help you navigate the ever-evolving world of Open Source. hangugyi guggihangugeo beojeonyi Collabora.com bogi Bandeira de PortuguesAcesse Collabora.com em Portugues Learn more * Who we are * Services * Our expertise * Industries * Our work * Careers * Open Source Collabora on LinkedIn Collabora on YouTube Collabora on Mastodon Collabora on Bluesky Collabora on Facebook Collabora RSS Feed +44 1223 362967 +1 514 667 2499 contact@collabora.com Open Since 2005 logo Our website only uses a strictly necessary session cookie provided by our CMS system. To find out more please follow this link. Collabora Limited (c) 2005-2025. All rights reserved. Privacy Notice. Sitemap. To ensure the Collabora website works as designed, please enable JavaScript. Thanks