21 ms·
Matt Godbolt sold me on Rust by showing me C++
- gbin 1y agoInterestingly in Rust I would immediately use an Enum for the Order! Way more powerful semantically.
- favorited 1y agoSide note, if anyone is interested in hearing more from Matt, he has a programming podcast with Ben Rady called Two's Complement. https://www.twoscomplement.org https://www.twoscomplement.org
- badbart14 1y ago+1, especially loved the episode from a couple months back about using AI tools in development. Really got me thinking differently about the role of AI in a developer's workflow and how software development will evolve.
- penguin_booze 1y agoI can't see the publish date on the episodes.
- chucksmash 1y agohttps://www.twoscomplement.org/#podcast/pair-programming-with-hal https://www.twoscomplement.org/#podcast/pair-programming-wit...
- markus_zhang 1y agoWhat if we have a C that removes the quirks without adding too much brain drain? So no implicit type conversions, safer strings, etc.
- wffurr 1y agoThis seems like such an obvious thing to have - where is it? Zig, Odin, etc. all seem much more ambitious.
- steveklabnik 1y agoThere have been attempts over the years. See here, a decade ago: https://blog.regehr.org/archives/1287 https://blog.regehr.org/archives/1287 > eventually I came to the depressing conclusion that there’s no way to get a group of C experts — even if they are knowledgable, intelligent, and otherwise reasonable — to agree on the Friendly C dialect. There are just too many variations, each with its own set of performance tradeoffs, for consensus to be possible.
- wffurr 1y agoThat was fascinating reading and a graveyard of abandoned "better C" dialects: SaferC, Friendly C, Checked C, etc.
- zyedidia 1y agoI think the only "C replacement" that is comparable in complexity to C is [Hare](https://harelang.org/ https://harelang.org/), but several shortcomings make it unsuitable as an actual C replacement in many cases (little/no multithreading, no support for macOS/Windows, no LLVM or GCC support, etc.).
- Zambyte 1y agoAnd why do you think Zig (and Odin, but I'm not really familiar with that one) is not comparable in complexity to C? If you start with C, replace the preprocessor language with the host language, replace undefined behavior with illegal behavior (panics in debug builds), add different pointer types for different types of pointers (single object pointers, many object pointers, fat many object pointers (slices), nullable pointers), and make a few syntactic changes (types go after the names of values in declarations, pointer dereference is a postfix operator, add defer to move expressions like deallocation to the end of the scope) and write a new standard library, you pretty much have Zig.
- writebetterc 1y agoYes, Rust is better. Implicit numeric conversion is terrible. However, don't use atoi if you're writing C++ :-). The STL has conversion functions that will throw, so separate problem.
- titzer 1y ago> Implicit numeric conversion is terrible. It's bad if it alters values (e.g. rounding). Promotion from one number representation to another (as long as it preserves values) isn't bad. This is trickier than it might seem, but Virgil has a good take on this (https://github.com/titzer/virgil/blob/master/doc/tutorial/Numbers.md https://github.com/titzer/virgil/blob/master/doc/tutorial/Nu...). Essentially, it only implicitly promotes values in ways that don't lose numeric information and thus are always reversible. In the example, Virgil won't let you pass "1000.00" to an integer argument, but will let you pass "100" to the double argument.
- plus 1y agoAside from the obvious bit size changes (e.g. i8 -> i16 -> i32 -> i64, or f32 -> f64), there is no "hierarchy" of types. Not all ints are representable as floats. u64 can represent up to 2^64 - 1, but f64 can only represent up to 2^53 with integer-level precision. This issue may be subtle, but Rust is all about preventing subtle footguns, so it does not let you automatically "promote" integers to float - you must be explicit (though usually all you need is an `as f64` to convert).
- mananaysiempre 1y ago> Aside from the obvious bit size changes (e.g. i8 -> i16 -> i32 -> i64, or f32 -> f64), there is no "hierarchy" of types. Depends on what you want from such a hierarchy, of course, but there is for example an injection i32 -> f64 (and if you consider the i32 operations to be undefined on overflow, then it’s also a homomorphism wrt addition and multiplication). For a more general view, various Schemes’ takes on the “numeric tower” are informative.
- dvratil 1y agoThe one thing that sold me on Rust (going from C++) was that there is a single way errors are propagated: the Result type. No need to bother with exceptions, functions returning bool, functions returning 0 on success, functions returning 0 on error, functions returning -1 on error, functions returning negative errno on error, functions taking optional pointer to bool to indicate error (optionally), functions taking reference to std::error_code to set an error (and having an overload with the same name that throws an exception on error if you forget to pass the std::error_code)...I understand there's 30 years of history, but it still is annoying, that even the standard library is not consistent (or striving for consistency). Then you top it on with `?` shortcut and the functional interface of Result and suddenly error handling becomes fun and easy to deal with, rather than just "return false" with a "TODO: figure out error handling".
- jasonjmcghee 1y agounfortunately it's not so simple. that's the convention. depending on the library you're using it might be a special type of Error, or special type of Result, something needs to be transformed, `?` might not work in that case (unless you transform/map it), etc. I like rust, but its not as clean in practice, as you describe
- koakuma-chan 1y agoYou can use anyhow::Result, and the ? will work for any Error.
- deleted 1y ago[deleted]
- deleted 1y ago[deleted]
- ryandv 1y agoThere are patterns to address it such as creating your own Result type alias with the error type parameter (E) fixed to an error type you own: type Result<T> = result::Result<T, MyError>; #[derive(Debug)] enum MyError { IOError(String) // ... } Your owned (i.e. not third-party) Error type is a sum type of error types that might be thrown by other libraries, with a newtype wrapper (`IOError`) on top. Then implement the `From` trait to map errors from third-party libraries to your own custom Error space: impl From<io::Error> for MyError { fn from(e: io::Error) -> MyError { MyError::IOError(e.to_string()) } } Now you can convert any result into a single type that you control by transforming the errors: return sender .write_all(msg.as_bytes()) .map_err(|e| e.into()); There is a little boilerplate and mapping between error spaces that is required but I don't find it that onerous.
- jpc0 1y agoAmazing example of how easy it is to get sucked into the rust love. Really sincerely these are really annoying parts of C++. The conversation function is more language issue. I don’t think there is a simple way of creating a rust equivalent version because C++ has implicit conversions. You could probably create a C++ style turbofish though, parse<uint32_t>([your string]) and have it throw or return std::expected. But you would need to implement that yourself, unless there is some stdlib version I don’t know of. Don’t conflate language features with library features. And -Wconversion might be useful for this but I haven’t personally tried it since what Matt is describing with explicit types is the accepted best practice.
- ujkiolp 1y agomeh, rust is still better cos it’s friendlier
- jpc0 1y agoI don’t disagree. Rust learnt a ton from C++. I have my gripes with rust, more it’s ecosystem and community that the core language though. I won’t ever say it’s a worse language than C++.
- noelnh 1y agoCould you elaborate on those points, I'm genuinely curious? So far, I have found the Rust community to be immensely helpful, much more so than I experienced the C++ community. Granted, that's quite some time ago and might be at least partially caused by me asking fewer downright idiotic questions. But still, I'm interested in hearing about your experiences.
- bigstrat2003 1y agoThe Rust community is helpful... but also quite political and extremely hostile to anyone who doesn't share those politics. Even something as anodyne as saying "let's keep politics out of technical discussion" is frequently met with hostility (because many community members believe that tech is inherently political and that trying to keep politics out is really just a bad faith attempt to frame things in terms of the requester's preferred politics). It's also full of drama in a way that other communities online simply aren't. For example, the drama that happened when the guy behind thephd.dev got invited to give the keynote at rustconf, then his talk was downgraded from the keynote - everyone involved in that mess (including other bloggers who weighed in) came off as immature and not someone you would ever want to work with. I like the Rust language quite a bit. I find the Rust community to be one of the most toxic places in the entire tech business. Your mileage may vary and that's fine of course - but plenty of people want to stay far away from a community that acts like the Rust community does.
- GardenLetter27 1y agoIt's a shame Rust doesn't have keyword arguments or named tuples to make handling some of these things easier without Args/Options structs boilerplate.
- shpongled 1y agoYep, I would love anonymous record types, ala StandardML/OCaml
- frankus 1y agoI work all day in Swift (which makes you go out of your way to omit argument labels) and I'm surprised they aren't more common.
- kevincox 1y agoYeah, this is one of the few things that I love about Swift. I think it gets it exactly right that keyword arguments should be the default and you can opt out in cases where the keyword is really unnecessary.
- jsat 1y agoHad the same thought... It's backwards that any language isn't using named parameters at this point.
- Ygg2 1y agoNamed parameters do come with a large footgun. Renaming your parameters is a breaking change. Especially if you're coming from different langs.
- const_cast 1y agoThis only really applies to languages that don't check this at compile-time. I don't consider compile-time errors a foot gun. I mean, it should be impossible for that kind of bad code to ever get merged in most reasonable CI/CD processes.
- morning-coffee 1y agoReading "The Rust Book" sold me on Rust (after programming in C++ for over 20 years)
- mixmastamyk 1y agoAm about finished, but several chapters near the end seriously put me to sleep. Will try again some other day I suppose.
- grumbel 1y agoThere is '-Wconversion' to catch things like this. It will however not trigger in this specific case since g++ assumes converting 1000.0 to 1000 is ok due to no loss in precision. Quantity(100) is counterproductive here, as that doesn't narrow the type, it does the opposite, it casts whatever value is given to the type, so even Quantity(100.5) will still work, while just plain 100.5 would have given an error with '-Wconversion'.
- Arnavion 1y agoThe reason to introduce the Quantity wrapper is to not be able to swap the quantity and price arguments.
- b5n 1y ago> -Wconversion ... assumes converting 1000.0 to 1000 is ok due to no loss in precision. Additionally, `clang-tidy` catches this via `bugprone-narrowing-conversions` and your linter will alert if properly configured.
- kelnos 1y agoMy opinion is that if you need to run extra tools/linters in order to catch basic errors, the language & its compiler are not doing enough to protect me from correctness bugs. I do run clippy on my Rust projects, but that's a matter of style and readability, not correctness (for the most part!).
- jpc0 1y agoHow much of what Rust the language checks is actually linter checks implemented in the compiler? Conversions may be fine and even useful in many cases, in this case it isn’t. Converting to std::variant or std::optional are some of those cases that are really nice.
- b5n 1y agoThere's a bit more nuance here than 'basic errors', and modern c compilers offer a lot of options _if you need to use them_. I appreciate that there are guardrails in a tool like rust, I also appreciate that sharp tools like c exist, they both have advantages.
- atemerev 1y agoRight. I attempted using Rust for trading-related code as well. However, I failed to write a dynamically linked always sorted order book where you can splice orders in the middle. It is just too dynamic for Rust. Borrow checker killed me. And don't get me started on dynamic graphs. I would happily use Rust over C++ if it had all other improvements but similar memory management. I am completely unproductive with Rust model.
- sunshowers 1y agoI apologize for the naive question, but that sounds like a heap?
- jpc0 1y agoIn my experience you need to approach this with vec or arrays of some sort and pass indices around… “We have pointers at home” behaviour. This is fine but coming from C++ it definitely feels weird…
- bigstrat2003 1y agoWhy not just use pointers? Rust has them, they aren't evil or anything. If you need to make a data structure that isn't feasible with references due to the borrow checker (such as a linked list), there's absolutely nothing wrong with using pointers.
- atemerev 1y agoAnd it will look like this: https://rust-unofficial.github.io/too-many-lists/sixth-final.html https://rust-unofficial.github.io/too-many-lists/sixth-final... (filled with boilerplate, strange Rust idioms, borrow_unchecked, phantomdata, and you still have to manage lifetimes annotations).
- bigstrat2003 1y agoAnd? I don't really see the issue. It works, it is sound, and it has a nice clean interface for safe code to use. That's all I really ask for. Lots of useful things in programming are quite gnarly under the hood, but that doesn't mean those things aren't worth using.
- adamc 1y agoComing from python (or Common Lisp, or...), I wasn't too impressed. In Python I normally make args for any function with more than a couple be keyword arguments, which guarantees that you are aware of how the arguments are being mapped to inputs. Even Rust's types aren't going to help you if two arguments simply have the same types.
- rq1 1y agoJust create dummy wrappers to make a type level distinction. A Height and a a Width can be two separate types even if they’re only floats basically. Or another (dummy) example transfer(accountA, accountB). Make two types that wrap the same type but one being a TargetAccount and the other SourceAccount. Use the type system to help you, don’t fight it.
- jpc0 1y agoDo you really want width and height or do you actually want dimensions or size? Same with transfer, maybe you wanted a transaction that gets executed. Worst case here use a builder with explicit function names.
- rq1 1y agoI don’t really understand your point there. Sound type systems are equivalent to proof systems. You can use them to design data structures where their mere eventual existence guarantee the coherence and validity of your program’s state. The basic example is “Fin n” that carries at compile time the proof that you made the necessary bounds checks at runtime or by construction that you never exceeded some bound. Some languages allow you to build entire type level state machines! (eg. to represent these transactions and transitions)
- jpc0 1y agoMy point is a Width type is usually not the sound type you are looking for. What probably wanted was a size type which is width and height. Or a dimensions type which is width and height. The problem was maybe not two arguments being confused but in reality a single thing with two elements…
- ModernMech 1y agoWhat sold me on Rust is that I'm a very bad programmer and I make a lot of mistakes. Given C++, I can't help but hold things wrong and shoot myself in the foot. My media C++ coding session is me writing code, getting a segfault immediately, and then spending time chasing down the reason for that happening, rinse and repeat. My median Rust coding session isn't much different, I also write code that doesn't work, but it's caught by the compiler. Now, most people call this "fighting with the borrow checker" but I call it "avoiding segfaults before they happen" because when I finally get through the compiler my code usually "just works". It's that magical property Haskell has, Rust also has it to a large extent. So then what's different about Rust vs. C++? Well Rust actually provides me a path to get to a working program whereas C++ just leaves me with an error message and a treasure map. What this means is that although I'm a bad programmer, Rust gives me the support I need to build quite large programs on my own. And that extends to the crate ecosystem as well, where they make it very easy to build and link third party libraries, whereas with C++ ld just tells you that it had a problem and you're left on your own to figure out exactly what.
- jpc0 1y agoUsing your media example since I have a decent amount of experience there. Did you just use off the shelf libraries, because effectively all the libraries are written in or expose a C api. So now you not only need to deal with Rust, you need to also deal with rust ffi. There are some places I won’t be excited to use rust, and media heavy code is one of those places…
- sophacles 1y agoGiven that the second paragraph starts with "my median rust..." i assume the "media C++" is actually a typo for "median C++".
- kanbankaren 1y ago> Given C++, I can't help but hold things wrong and shoot myself Give an example. I have been programming in C/C++ for close to 30 years and the places where I worked had very strict guidelines on C++ usage. We could count the number of times we shot ourselves due to the language.
- hacker_homie 1y agoWconversion And werror?
- skywal_l 1y agoI love his videos on computerphile. One was just dropped a few hours ago: https://www.youtube.com/watch?v=1su3lAh-k4o https://www.youtube.com/watch?v=1su3lAh-k4o
- jsat 1y agoI see an article about how strict typing is better, but what would really be nice here is named parameters. I never want to go back to anonymous parameters.
- kelnos 1y agoYes, this is one of the few things that I think was a big mistake in Rust's language design. I used to do a lot of Scala, and really liked named parameters there. I suppose it could still be added in the future; there are probably several syntax options that would be fully backward-compatible, without even needing a new Rust edition.
- quietbritishjim 1y agoI suppose the sense it is backwards incompatible is that library authors have named their parameters without intended to make them part of the public interface that they commit to maintaining. Perhaps it could be made backwards compatible by being opt in function declarations but that would seem like a bit of a pain.
- codedokode 1y agoWhen there are 3-4 parameters it is too much trouble to write the names.
- noitpmeder 1y agoNot OP, but I imagine he's arguing for something like python's optional named arguments.
- bsder 1y ago> When there are 3-4 parameters it is too much trouble to write the names. Sorry, I don't agree. First, code is read far more often than written. The few seconds it takes to type out the arguments are paid again and again each time you have to read it. Second, this is one of the few things that autocomplete is really good at. Third, almost everybody configures their IDE to display the names anyway. So, you might as well put them into the source code so people reading the code without an IDE gain the benefit, too. Finally, yes, they are redundant. That's the point. If the upstream changes something and renames the argument without changing the type I probably want to review it anyway.
- no-marxism-thx 1y ago[dead]
- codr7 1y ago[flagged]
- tumdum_ 1y agoThe one thing that sold me on Rust was that I no longer had to chase down heisenbugs caused by memory corruption.
- monkeyelite 1y agoWhat are you responding to from the article?
- Winsaucerer 1y agoThe title.
- kasajian 1y agoThis seems a big silly. This is not a language issue. You can have a C++ library that does exactly all the things being shown here so that the application developer doesn't worry about. There would no C++ language features missing that would accomplish what you're able to do on the Rust side. So is this really a language comparison, or what libraries are available for each language platform? If the latter, that's fine. But let's be clear about what the issue is. It's not the language, it's what libraries are included out of the box.
- Etheryte 1y agoJust like language shapes the way we think and talk about things, programming languages shape both what libraries are written and how. You could write anything in anything so long as it's Turing complete, but in real life we see clearly that certain design decisions at the language level either advantage or disadvantage certain types of solutions. Everyone could in theory write C without any memory issues, but we all know how that turns out in practice. The language matters.
- Maxatar 1y agoThe Sapir Whorf hypothesis has long been debunked: https://en.m.wikipedia.org/wiki/Linguistic_relativity https://en.m.wikipedia.org/wiki/Linguistic_relativity
- oasisaimlessly 1y agoThe strong hypothesis has been debunked, yes, but nobody is asserting it. From your link: > Nevertheless, research has produced positive empirical evidence supporting a weaker version of linguistic relativity:[5][4] that a language's structures influence a speaker's perceptions, without strictly limiting or obstructing them.
- milesrout 1y ago[dead]
- lytedev 1y ago
- codedokode 1y agoWhat about catching integer overflow? Free open-source languages still cannot do it unlike they commercial competitors like Swift?
- lytedev 1y agoI'm not sure if this is what you mean, exactly, but Rust indeed catches this at compile time. https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=7e9037378ca64a7608ca4fee12d34e67 https://play.rust-lang.org/?version=stable&mode=debug&editio... https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=e5478a27f7ce77e76e411f7039671b53 https://play.rust-lang.org/?version=stable&mode=debug&editio...
- codedokode 1y agoI meant panic if during any addition (including in runtime) an overflow occurs.
- trealira 1y agoYou can set a flag for that: https://doc.rust-lang.org/rustc/codegen-options/index.html#overflow-checks https://doc.rust-lang.org/rustc/codegen-options/index.html#o... By default, they're on during debug mode and off in release mode.
- codedokode 1y agoThe choice doesn't make sense because you want the program to always behave correctly and not only during development.
- lytedev 1y agoEh, maybe. There's a performance tradeoff here and maintainers opted for performance. I'm sure many folks would agree with you that it was the wrong choice, and I'm sure many folks would disagree with you that it was the wrong choice. There are also specific methods for doing *erflow-checked arithmetic if you like.
- mempko 1y agoRust does seem to have a lot of nice features. My biggest blocker for me going to Rust from C++ is that C++ has much better support for generic programming. And now that Concepts have landed, I'm not aware of any language that can compete in this area.
- thrwyexecbrain 1y agoThe C++ code I write these days is actually pretty similar to Rust: everything is explicit, lots of strong types, very simple and clear lifetimes (arenas, pools), non-owning handles instead of pointers. The only difference in practice is that the build systems are different and that the Rust compiler is more helpful (both in catching bugs and reporting errors). Neither a huge deal if you have a proper build and testing setup and when everybody on your team is pretty experienced. By the way, using "atoi" in a code snippet in 2025 and complaining that it is "not ideal" is, well, not ideal.
- yodsanklai 1y ago> The C++ code I write these days Meaning you're in a context where you have control on the C++ code you get to write. In my company, lots of people get to update code without strict guidelines. As a result, the code is going to be complex. I'd rather have a simpler and more restrictive language and I'll always favor Rust projects to C++ ones.
- bluGill 1y agoThat is easy to say today, but I guarantee in 30 year Rust will have rough edges too. People always want some new feature and eventually one comes in that cannot be accommodated nicely. Of course it will probably not be as bad as C++, but still it will be complex and people will be looking for a simpler language.
- timbit42 1y agoHow many rough edges will C++ have in another 30 years?
- bluGill 1y agoWho knows. It will likely have more than any other language. Though it will also continue to not get credit for things it got right. There will always remain two types of languages: those that nobody uses and those that everybody complains about.
- mattgodbolt 1y agoWow that guy, eh? He seems to turn up everywhere :D
- oconnor663 1y agoThis account's been pretending to be Matt Godbolt since 2014. Knows him better than he knows himself. Absolute commitment to the bit.
- antirez 1y agoYou can have two arguments that are semantically as distinct and important as quantity and price and be both integers, and if you swap them is a big issue anyway. And you would be forced, if you like this kind of programming, to create distinct types anyway. But I never trust this kind of "toy" defensive programming. The value comes from testing very well the code, from a rigorous quality focus.
- spyrja 1y agoTo be fair, this sort of thing doesn't have to be so much worse in C++ (yes, it would have been nice if it had been built into the language itself to begin with). You just need a function to do a back-and-forth conversion which then double-check the results, ie: #include <exception> #include <sstream> template <typename From, typename To> void convert_safely_helper_(From const& value, To& result) { std::stringstream sst; sst << value; sst >> result; } // Doesn't throw, just fails template <typename From, typename To> bool convert_safely(From const& value, To* result) { From check; convert_safely_helper_(value, *result); convert_safely_helper_(*result, check); if (check != value) { *result = To(); return false; } return true; } // Throws on error template <typename To, typename From> To convert_safely(From const& value) { To result; if (!convert_safely(value, &result)) throw std::logic_error("invalid conversion"); return result; } #include <iostream> template <typename Buy, typename Quantity, typename Price> void sendOrder(const char* symbol, Buy buy, Quantity quantity, Price price) { std::cout << symbol << " " << convert_safely<bool>(buy) << " " << convert_safely<unsigned>(quantity) << " " << convert_safely<double>(price) << std::endl; } #define DISPLAY(expression) \ std::cout << #expression << ": "; \ expression template <typename Function> void test(Function attempt) { try { attempt(); } catch (const std::exception& error) { std::cout << "[Error: " << error.what() << "]" << std::endl; } } int main(void) { test([&] { DISPLAY(sendOrder("GOOG", true, 100, 1000.0)); }); test([&] { DISPLAY(sendOrder("GOOG", true, 100.0, 1000)); }); test([&] { DISPLAY(sendOrder("GOOG", true, -100, 1000)); }); test([&] { DISPLAY(sendOrder("GOOG", true, 100.5, 1000)); }); test([&] { DISPLAY(sendOrder("GOOG", 2, 100, 1000)); }); } Output: sendOrder("GOOG", true, 100, 1000.0): GOOG 1 100 1000 sendOrder("GOOG", true, 100.0, 1000): GOOG 1 100 1000 sendOrder("GOOG", true, -100, 1000): GOOG 1 [Error: invalid conversion] sendOrder("GOOG", true, 100.5, 1000): GOOG 1 [Error: invalid conversion] sendOrder("GOOG", 2, 100, 1000): GOOG [Error: invalid conversion] Rust of course leaves "less footguns laying around", but I still prefer to use C++ if I have my druthers.
- pjmlp 1y agoYes, from safety point of view Rust is much better option, however from the ecosystems I care about (language runtimes and GPU coding), both professionally and as hobby, C++ is the systems language to go, using Rust in such contexts would require me to introduce extra layers and do yak shaving instead of the actual problem that I want to code for.
- nyanpasu64 1y agoThe problem I've always had with unit type wrappers is you can't convert between a &[f32] and a &[Amplitude<f32>] like you can convert a single scalar value.
- fvncc 1y agoThere are libraries that help with these conversions. See e.g.: https://docs.rs/bytemuck/latest/bytemuck/trait.TransparentWrapper.html https://docs.rs/bytemuck/latest/bytemuck/trait.TransparentWr...
- conradludgate 1y agoAs long as the wrapper is marked as transparent, it's safe to transmute the wrappers. If you don't want unsafe, you can make use of this safe derive: https://docs.rs/bytemuck/latest/bytemuck/trait.TransparentWrapper.html https://docs.rs/bytemuck/latest/bytemuck/trait.TransparentWr...
- simpaticoder 1y agoI don't get it. Isn't this a runtime problem and not a compile-time problem? buy() or sell() is going to be called with dynamic parameters at runtime, in general. That is, calls with concrete values are NOT going to be hard-coded into your program. I would write the function to assert() invariants within the function, and avoid chasing compile-time safety entirely. If parameter order was a concern, then I'd modify the function to take a struct, or similar.
- brundolf 1y ago> Isn't this a runtime problem and not a compile-time problem? buy() or sell() is going to be called with dynamic parameters at runtime, in general. Yes, but the strength of Rust's type system means you're forced to handle those bad dynamic values up front (or get a crash, if you don't). That means the rest of your code can rest safe, knowing exactly what it's working with. You can see this in OP's parsing example, but it also applies to database clients and such
- simpaticoder 1y agoWhat if the valid input for quantity must be greater than 0? A reasonable constraint, I think. The OP's example is contrived to line up with Rust's built-in types, and ignores the general problem.
- siev 1y agoRust also has the standard NonZero<T> type for that use case.
- brundolf 1y agoIt's a common fallacy to equate "there's a limit to how much we can guarantee" with "guaranteeing anything is a waste of time". Each guarantee we can make eliminates a whole class of possible bugs That said, Rust also makes it very easy to define your own types that can only be constructed/unpacked in limited ways, which can enforce special constraints on their contents. And it has a cultural norm of doing this in the standard library and elsewhere Eg: a sibling poster noted the NonZero<T> type. Another example is that Rust's string types are guarantees to always contain valid UTF-8, because whenever you try and convert a byte array into a string, it gets checked and possibly rejected.
- brundolf 1y agoWait, Godbolt is someone's name?
- frankwiles 1y agoYes and he’s a really cool nice guy to boot!
- brundolf 1y agoI almost want to call this nominative determinism I always thought it was called godbolt because it's like... Zeus blowing away the layers of compilation with his cosmic power, or something. Like it's a herculean task
- tialaramex 1y agoAnother example: eBay is because its founders solo consulting business "Echo Bay Technology Group" owned ebay.com and so when he built his auction web site on that domain everybody just called the auction site "eBay" anyway.
- socalgal2 1y agoI already hated C++ (having written 100s of thousands of lines of it in games and at FAANG) I'd be curious to know what if any true fixes are coming down the line. This talk: "To Int or to Uint, This is the Question - Alex Dathskovsky - CppCon 2024" https://www.youtube.com/watch?v=pnaZ0x9Mmm0 https://www.youtube.com/watch?v=pnaZ0x9Mmm0 Seems to make it clear C++ is just broken. That said, and I wish he'd covered this, he didn't mention if the flags he brings up would warn/fix these issues. I don't want a C++ where I have to remember 1000 rules and if I get one wrong my code is exploitable. I want a C++ where I just can't break the rules except when I explicitly opt into breaking them. speaking of which, according to another C++ talk, something like 60% of rust crates are dependent on unsafe rust. The point isn't to diss rust. The point is that a safe C++ with opt into unsafe could be similar to rust's opt into unsafe
- eslaught 1y agoThere has been talk of new language frontends for C++: Cpp2 (Herb Sutter's brainchild): https://hsutter.github.io/cppfront/ https://hsutter.github.io/cppfront/ Carbon (from Google): https://github.com/carbon-language/carbon-lang https://github.com/carbon-language/carbon-lang In principle those could enable a safe subset by default, which would (except when explicitly opted-out) provide similar safety guarantees to Rust, at least at the language level. It's still up to the community to design safe APIs around those features, even if the languages exist. Rust has a massive advantage here that the community built the ecosystem with safety in mind from day 1, so it's not just the language that's safe, but the APIs of various libraries are often designed in an abuse-resistant way. C++ is too much of a zoo to ever do that in a coherent way. And even if you wanted to, the "safe" variants are still in their infancy, so the foundations aren't there yet to build upon. I don't know what chance Cpp2 or Carbon have, but I think you need something as radical as one of these options to ever stand a chance of meaningfully making C++ safer. Whether they'll take off (and before Rust eats the world) is anyone's guess.
- aw1621107 1y agoI don't think Carbon is a C++ frontend like cppfront. My impression is that cppfront supports C++ interop by transpiling/compiling to C++, but Carbon compiles straight to LLVM and supports C++ interop through built-in language mechanisms.
- xarope 1y agoThis reminds me of SQL's constraints, or pydantic's custom types and validators, which can validate that a value should be an int, and between 0-999, and not exceed, e.g. -1 or 1000. pydantic is a library for python, but I'm not aware of anything similar in rust or golang that can do this yet? (i.e. not just schema validation, but value range validation too)
- jayong93 1y ago[dead]
- kstrauser 1y agoThe semantics are a little different in that you have to explicitly call .validate(), but Rust's "validator" crate[0] feels a lot like Pydantic to me. [0]https://docs.rs/validator/latest/validator/ https://docs.rs/validator/latest/validator/
- xarope 1y agoI'm aware of this as well as go(lang)'s https://github.com/go-playground/validator https://github.com/go-playground/validator, so you are right, I'm wondering about a more implicit validator like what Pydantic does when a value is assigned, versus an explicit call to validate().
- nailer 1y ago> 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. OK but "this makes for a nice example" is silly, given that the only reason the example throws an error is that you used a float here, when both `quantity` and `price` would have been ints. 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}` I love Rust, but this is artificial.
- tialaramex 1y agoWhy are they "ints" ? One of the first things we realise, in both C++ and Rust, is that we mostly don't want these primitives like "int", we want our own user defined types, and Rust is better at that in practice. In the C and C++ people tend to actually write the file descriptor will be an int, and the timeout will be an int, and the user account number will be an int, and the error code will be an int... because the language doesn't help much when you don't want that. In the Rust people actually write the file descriptor will be an OwnedFd (from the stdlib) and the timeout will be a Duration (from the stdlib), and user account number might be their own AcctNo and that error code is maybe MyCustomError This is a language ethos thing, C++ got string slices after Rust despite the language being much older. String slices which are a really basic central idea, but eh, C++ programmers a decade ago just had char * pointers instead and tried not to think about it too much. Still today plenty of C++ APIs don't use string slices, don't work with a real duration type, and so on. It's technically possible but the language doesn't encourage this. What C++ does encourage is magic implicit conversion, as with this f64 versus i64 case.
- nailer 1y agoHah, it’s interesting - I program Rust mainly for resource constrained environments and everyone uses primitives. This was a good insight into how other people are thinking.
- TnS-hun 1y agoExamples miss angle brackets. For example: template explicit Quantity(T quantity) : m_quantity(quantity) { sendOrder("GOOG", false, Quantity(static_cast(atoi("-100"))),
- time4tea 1y agoaka use Tiny Types, they will help you. Been true in all statically typed languages for decades! It's good advice.
- choeger 1y agoAll this has been known in the PL design community for decades if not half a century by now. Two things are incredibly frustrating when it comes to safety in software engineering: 1. The arrogance that "practitioners" have against "theorists" (everyone with a PhD in programming languages) 2. The slowness of the adoption of well-tested and thoroughly researched language concepts (think of Haskell type classes, aka, Rust traits) I like that Rust can pick good concepts and design coherent language from them without inventing its own "pragmatic" solution that breaks horribly in some use cases that some "practitioners" deem "too theoretical."
- Ygg2 1y ago> I like that Rust can pick good concepts and design coherent language from them without inventing its own "pragmatic" solution that breaks horribly in some use cases that some "practitioners" deem "too theoretical." I've thought Rust picked some pretty nifty middle ground. On one side, it's not mindfucking unsafe like C. It picked to remove a set of problems like memory safety. On the other side, Rust didn't go for the highest of theoretical grounds. It's not guaranteeing much outside of it, and it also relies a bit on human help (unsafe blocks).
- eptcyka 1y agoAs per the article, Rust has benefits beyond the ones afforded by the borrow checker.
- Ygg2 1y agoSure, but it is pragmatic in other ways as well :) It takes ADT, but not function currying, and so on.
- jeffparsons 1y agoI've occasionally wondered about the lack of currying in Rust; it feels like something that can be done mechanically at compile time, so why not support it? Perhaps to do it cleanly (without more magical privileged functions in core) would require variadic generics?
- karel-3d 1y agoWell, if you don't want to confuse parameters, you should use Objective-C. You would do [orderbook sendOrderWithSymbol:"foo" buy:true quantity:100 price:1000.00] Cannot confuse that! (I never used swift, I think it retains this?)
- int_19h 1y agoSwift does retain this, but it makes it look much more readable. The example above would be something like: orderbook.sendOrder(symbol: "foo", buy: true, quantity: 100, price: 1000.00); It also has a nifty syntax for function declarations to allow decoupling the keyword from the variable name when that leads to more readable code: func parseInt(from s: String) -> Int { /* the variable is named 's' here */ } parseInt(from: "123")
- feverzsj 1y agoThe C++ code is just pure nonsense. If you really want to be fool-proofing, C++ offers concept, which is far superior to what rust offers.
- mansoor_ 1y agoFYI newer builds of GCC have this functionality.
- globalnode 1y agoMy admittedly uninformed impression of Rust is that its a lot like Go (in spirit?), a language invented to shepherd novice programmers into not making mistakes with resource usage. I imagine faceless shameless mega-corps with thousands of Rust/Go peons coding away on the latest soulless business apps. Designed to funnel the ignorant masses down corridors of dark pattern click bait and confusing UX. Having exposed my biases, happy to be proven wrong. Why are game studios still using C++? Because that's the language game programmers know and feel comfortable with? Or some other reason? Embedded is still C, games are C++, scientific and data are Python and R (I'm talking in general here). What is the niche for Rust?
- simonask 1y agoNovice programmers will take longer to be productive in Rust compared to Go. Rust primarily improves the productivity of people who know what they are doing, because it gives them much better tools to manage complexity. Games are written in C++ because game engines and tooling have person-centuries of work poured into them. Reimplementing Unreal Engine in Rust would require another few person-centuries of work, which is an investment that doesn't really make sense. Economically, dealing with the shortcomings of C++ is much, much cheaper. But Rust is definitely encroaching in all of these areas. Embedded Rust is doing great, scientific Rust is getting there (check pola.rs). Rust is an obvious candidate for the next big game engine, and it is already quite viable for indie undertakings, though it is still early days.
- amai 1y agoThough in the future people might simply ask an AI to convert a codebase from C++ to Rust.
- chickenbuckcar 1y agoEconomic inertia alone can already enough. Numpy use C/C++ because BLAS use C/C++ Torch originally use Lua, then switch to Python because popularity
- gwd 1y ago> novice programmers I think Rust has too high a learning curve, and too many features, for novice programmers in general. > Embedded is still C, games are C++, scientific and data are Python and R (I'm talking in general here). What is the niche for Rust? Rust has already made huge inroads in CLIs and TUIs, as far as I can tell. Embedded is a slow-moving beast by design, but it seems to me (as someone in an adjacent area) that it could be a big win there, particularly in places that need safety certification. All the stories of people using Rust for game development are about people who tried it and find that it doesn't fit: It makes experimentation and exploration slow enough that the reduction in minor bugs in game logic isn't really worth it.
- TinkersW 1y agoDunno about this, the example C++ code is so obviously bad that I had no desire to watch the video. Creating strong types for currency seems like common sense, and isn't hard to do. Even the Rust code shouldn't be using basic types.
- jk3000 1y agoSomehow this is the pattern when comparing C++ to Rust: write outrageously bad C++ in the first place, then complain about it.
- quietbritishjim 1y agoI always enjoy reading articles like this. But the truth is, having written several 100s of KLOC in C++ (i.e., not an enormous amount but certainly my fair share) I just almost never have problems with this sort accidental conversion in practice. Perhaps it might trip me up occasionally, but will be noticed by literally just running the code once. Yes, that is an extra hurdle to trip over and resolve but that is trivial compared to the alternative of creating and using wrapper types - regardless of whether I'm using Rust or C++. And the cost of visual noise of wrapper types, already higher just at the writing stage, then continues to be a cost every time you read the code. It's just not worth it for the very minor benefit it brings. (Named parameters would definitely be great, though. I use little structs of parameters where I think that's useful, and set their members one line at a time.) I know that this is an extremist view, but: I feel the same way about Rust's borrow checker. I just very rarely have problems with memory errors in C++ code bases with a little thought applied to lifetimes and use of smart pointers. Certainly, lifetime bugs are massively overshadowed by logic and algorithmic bugs. Why would I want to totally reshape the way that I code in order to fix one of the least significant problems I encounter? I actually wish there were a variant of Rust with all its nice clean improvements over C++ except for lifetime annotations and the borrow checker. Perhaps this is a symptom of the code I tend to write: code that has a lot of tricky mathematical algorithms in it, rather than just "plumbing" data between different sources. But actually I doubt it, so I'm surprised this isn't a more common view.
- spacechild1 1y ago> Why would I want to totally reshape the way that I code in order to fix one of the least significant problems I encounter? I feel the same. Rust certainly has many nice properties and features, but the borrow checker is a huge turn-off for me.
- viraptor 1y ago> but will be noticed by literally just running the code once. I assure you that's not the case. Maybe you didn't make that mistake, but if you did I'm sure it sometimes went unnoticed. I've found those issues in my code and in other projects. Sometimes they even temporarily don't matter, because someone did a func(CONST, 0) instead of func(0, CONST) and it turns out CONST is 0 - however the next person gets a crash because they change 0 to 1. A lot of similar issues come from the last line effect https://medium.com/@Code_Analysis/the-last-line-effect-7b1cb7f0d2a1 https://medium.com/@Code_Analysis/the-last-line-effect-7b1cb... and can last for years without being noticed.
- zombot 1y agoTypes are a catastrophe in C++ that cannot be fixed, no matter how much cruft you bolt on after the fact. It's time to leave this dinosaur and use a sane language.
- monkeyelite 1y agoThis article compares onlye one specific feature of C++ with Rust - integral type conversions.
- tialaramex 1y agoAll of C++ has implicit type conversions. The language even has a keyword (explicit) to try to reign this in because it's so obviously a bad idea. In Rust what they'd do if they realised there's a problem like this is make explicit conversion the default, with a language Edition, and so within a few years just everybody is used to the improved language. In C++ instead you have to learn to write all the appropriate bugfix keywords all over your software, forever.
- monkeyelite 1y ago> because it's so obviously a bad idea. Agreed. The history here is compatibility with C type conversion. I just expected a more compelling Rust /C++ comparison but we got an emphasis of a poorly designed feature which the standard has taken steps to improve already.
- tialaramex 1y agoNo, implicit conversion is a deliberate C++ feature and no analog existed in C. Like a lot of awful things about C++ this is their own choice and it's frustrating that they try to blame C for their choices. In C++ when we define a class Foo (a thing which doesn't exist in C) and we write a constructor Foo(Bar x) (which doesn't exist in C) which takes a single parameter [in this case a Bar named x], that is implicitly adopted as a conversion for your new user defined type and by default without any action on your part the compiler will just invoke that constructor to make a Bar into a Foo whenever it thinks that would compile. This is a bad choice, and it's not a C choice, it's not about "compatibility".
- monkeyelite 1y ago> No, implicit conversion is a deliberate C++ feature and no analog existed in C No. > it's not a C choice, it's not about "compatibility". One of the design of C++ classes is that you can create a class as powerful as int - you can’t do that without implicit conversion.
- amai 1y agoADA has solved these issues since the 80s: https://learn.adacore.com/courses/Ada_For_The_CPP_Java_Developer/chapters/05_Type_System.html#strong-typing https://learn.adacore.com/courses/Ada_For_The_CPP_Java_Devel...
- DrBazza 1y agoThe implicit problem here (pun intended) in the given examples are implicitness vs. explicitness. Rust chose (intentionally or otherwise) to do the opposite of the many things that C++ does, because C++ does it wrong. And C++ does it wrong because we didn't know any better at the time, and the world, pre-internet, was much less connected. Someone had to do it first (or first-ish). The main thing I like about Rust is the tooling. C++ is death by a thousand build systems and sanitizers.
- Calliope1 1y ago[flagged]
- kccqzy 1y agoUndefined behavior can appear with or without templates. But other than that, yes, it is viscerally shocking to see on Compiler Explorer that your entire function has been compiled to a single instruction, ud2.
- Calliope1 1y agoSeeing an entire function compiled down to a single ud2 instruction in Compiler Explorer was the moment I truly grasped the power of undefined behavior. Templates are just tools — but undefined behavior (UB) is the real shadow lord. That seemingly innocent line of C++ code you wrote? The compiler might decide it has no meaning at all and emit ud2 — a deliberate “invalid operation” that crashes the program. This isn’t just a language “gotcha”; it’s the compiler shouting: “Your code makes no sense to me — so I’m terminating it with prejudice.” It’s both technically fascinating and philosophically unsettling. Who should bear the burden of balancing performance and safety? Should we be more careful developers, or should the language take more responsibility? Or… is this precisely the case for Rust? The compiler didn’t betray you. It simply honored your undefined contract.
- nmeofthestate 1y agoIf I tried to repro this problem in C++ code I believe it would fail clang tidy checks because of the implicit numeric casts.
- Horffupolde 1y agoWould this also work for Zig?
- deleted 1y ago[deleted]
- jovial_cavalier 1y agoHey, check this out: #include <iostream> struct Price { double x; }; struct Quantity { int x; }; void sendOrder(const char *symbol, bool buy, Quantity quantity, Price price) { std::cout << symbol << " " << buy << " " << quantity.x << " " << price.x << std::endl; } int main(void) { sendOrder("GOOG", false, Quantity{100}, Price{1000.00}); // Correct sendOrder("GOOG", false, Price{1000.00}, Quantity{100}); // compiler error } If you're trying to get it to type check, you have to make a type first. I don't appreciate these arguments, and view them as disingenuous.
- legobmw99 1y agoThat still has the issue of Quantity{-100} being a-ok as far as the compiler is concerned, but there are other things one can do (as the article alludes to). My reading of this article wasn't to say that these things are impossible in C++, just that they're not the default or the first thing you try as a beginner is perfectly wrong.
- bunderbunder 1y agoThis is actually the point where Rust starts to frustrate me a little bit. Not because Rust is doing anything wrong here, but because the first well-known language to really get some of these things right also happens to be a fairly low-level systems language with manual memory management. A lot of my colleagues seem to primarily be falling in love with Rust because it's doing a good job at some basic things that have been well-known among us "academic" functional programming nerds for decades, and that's good. It arguably made inroads where functional programming languages could not because it's really more of a procedural language, and that's also good. Procedural programming is a criminally underrated and misunderstood paradigm. (As much as I love FP, that level of standoffishness about mutation and state isn't any more pragmatic than OOP being so hype about late binding that every type must support it regardless of whether it makes sense in that case.) But they're also thoroughly nerdsniped by the borrow checker. I get it, you have to get cozy with the borrow checker if you want to use Rust. But it seems like the moral opposite of sour grapes to me. The honest truth is that, for most the software we're writing, a garbage collected heap is fine. Better, even. Shared-nothing multithreading is fine. Better, even. So now we're doing more and more things in Rust. Which I understand. But I keep wishing that I could also have a Rust-like language that just lets me have a garbage collector for the 95% of my work where the occasional 50ms pause during run-time just isn't a big enough problem to justify a 50% increase in development and maintenance effort. And then save Rust for the things that actually do need to be unmanaged. Which is maybe 5% of my actual work, even if I have to admit that it often feels like 95% of the fun.
- legobmw99 1y agoBased on the rest of your comment I suspect you're already familiar, but a decent candidate for "Rust with a GC" is OCaml, the language the first Rust compiler was written in.
- bunderbunder 1y agoIt's close. Perhaps there's an interesting conversation to be had about why OCaml hasn't taken over the world the way Rust has. The toolchain might be a first candidate. Rust's toolchain feels so very modern, and OCaml's gives me flashbacks to late nights trying to get my homework done on the department's HP-UX server back in college.
- mrsofty 1y agoGodbolt is a legend and I don't care what the others say. I believe him when he said he was "just helping" that sheep through the hedge. Well deserved praise Matty-poo and well written article.
- juliangmp 1y ago> 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! I keep a list of "additional" error codes for my work with GCC and its at least two dozen parameters. The fact that some of these aren't the default is outrageous to me. And Wconversion is one of those things that seemingly no one uses so every open source library I include gives me those errors.
- mmaniac 1y ago> 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. I think that's the essential point, really... It'd be hard to argue that the rest of Rust isn't overall "better" than C++, but the compromises made to flexibility and ergonomics to achieve memory safety in Rust are the biggest points of contention for Rust critics.