4 ms·
Which aspect(s) of Rust do you think are most responsible for this? (e.g. borrow checker, memory safety, culture that attracts devs who care about reliability,
by rkagerer 4y ago
Which aspect(s) of Rust do you think are most responsible for this? (e.g. borrow checker, memory safety, culture that attracts devs who care about reliability, etc)
- gpm 4y agoNot the person you're asking, but the culture around data representation strikes me as the biggest factor: 1. Only making valid states possible, to the greatest extent reasonably possible. 2. Treating error as regular data, not an afterthought - with language features to make that not too painful. 3. Not returning placeholder values (i.e. if you get back a parsed value from a parse function, it means it parsed correctly, not that either there's an error somewhere else or it parsed correctly). Language features, in particular "enums" (aka algebraic data types, aka tagged unions), make this approach possible. You couldn't do it in go, for instance, even if there was a cultural decision to.
- pornel 4y agoIt is of course a combination of all these aspects. A type system that can express thread safety (Send/Sync traits) is incredibly valuable when building multi-threaded systems. Universal definition of what is safe, and standard traits and borrowing rules, make APIs more predictable. Just from function's signature you know a lot about its behavior, without having to look for gotchas in the manual. Mandatory error handling prevents cutting corners. Unit testing is built-in. Generics, good inlining, and Cargo help split code into libraries without a performance or usability hit, which helps make focused, well-tested components. Most of these things aren't groundbreaking, but Rust being new had a luxury of picking current best practices and sensible defaults.
- nicoburns 4y agoA few things: - I think memory safety is a baseline. You'll note that memory safe languages already tend to be much more reliable than non-memory-safe languages in general. - Then you have the error handling. A lot of unreliability in my code in other languages comes from unhandled exceptions that only occur rarely. Rust generally puts all possible error conditions in the type signature of the function. Meaning it's actually feasible to handle every failure case. - Speaking of unhandled exceptions, a lot of those in typed languages tend to be caused by null. Rust does not have null. Instead it has Option, and it is impossible to access the contents of an option without doing the equivalent of a null check. So that entire class of errors is gone. - Both Result (used for error handling) and Option (used instead of null) are what Rust calls enums, and what are more generally called Sum Types. I think these are a huge deal. They allow you to safely represent data that may be one thing or another with very strict type checking. These are broadly very useful in API design, and in my experience lead to much more robust code than the class hierarchies you need in OOP languages or unions which lack the safety checks. (Aside: sum types would be quite simple to add to other languages. I have no idea why they haven't been added yet). - Speaking of classes, inheritance is not supported. So that's a bunch of confusing code that just isn't possible to write. This can add a bit of boilerplate to Rust code, but it makes it more straightforward and less bug prone. - You mentioned the borrow checker. That definitely helps. It's yet another tool that allows you to write APIs that cannot be misused. A great example would be Rust's Mutex type. It can prove at compile time that code does not hold on to references to the protected data beyond the duration that the lock is held. - Speaking of Mutex. Rust's Send and Sync traits provide very good thread safety. You almost don't need to worry about thread safety at all in Rust. Most concurrency bugs are prevented by the compiler (you can still do things like cause data races). - Newtypes allow you to check invariants once and then have the fact that they remain satisfied enforced by the type system. - All type casts are explicit. - Lots of other little things One final thing that I think is often overlooked. Rust is strict, and all of these checks apply not only to the code your write, but to all of your dependencies. That means that Rust libraries tend to be much more reliable than libraries from other ecosystems. That probably is partly because of a culture of reliability. But it's also because the language itself makes it hard to write sloppy code. And that the code you are building on is likely to be reliable makes it both less effort and more worthwhile to make your own code reliable (including for library authors), leading to virtuous circle of reliable code.
- tomaszsobota 4y agoThanks for taking the time to write it down, appreciate it.
- otikik 4y agoI think ultimately the big thing Rust brings to the table is that a lot of Rust's features is geared towards detecting problems as early as possible. This means you get complexity, especially on the "initial phases". * The initial learning curve is steeper than usual . You have x types of string instead of one or two, you have lifetimes, etc, etc. Fortunately you pay it once and then can use it many times (maybe with some revisits to the docs :P) * The initial building of a feature might take a bit longer than in other less complex languages (the borrow checker gives you a particularly tricky error, etc). But what is happening here is that the language is picking up problems that other languages ... don't. They "defer to production". Others are mentioning that cargo is another feature that helps. I think it does, but indirectly. What cargo does is improving the developer experience. Having all the packages at the tip of your fingers, without needing to jump over hoops like in other languages is just ... nice. Given the initial ramp up in complexity, and the initial harder-than-usual first write, anything that improves the UX goes towards alleviating that. I personally would put cargo on the same bucket as "helpful compiler messages" and "good docs".
- 0x457 4y agoAgree with you on most except for one point: > You have x types of string instead of one or two Funny how you said X, while there are really only 3 types of strings in standard library. You generally only encounter 2 of them: String and OsString. OsString, while annoying, but it's one of the things that makes rust safe. You only ever deal with 3rd type, CString, when you work with FFI, at which point you better already know how strings work on C.
- nicoburns 4y agoThere is also PathBuf, which would use a string in most languages. And sometimes one may need to work with Vec<u8> or Vec<Char>. And then slice/view variants of all the above.
- manholio 4y ago> And then slice/view variants of all the above. So all in all, about a dozen kinds of strings.