Great things about Rust that aren't just performance
ntietz.com195 points by vortex_ape 6 months ago
195 points by vortex_ape 6 months ago
For me, there's a headline draw, which is the borrow checker. Really great.
But apart from that, Rust is basically a bag of sensible choices. Big and small stuff:
- Match needs to be exhaustive. When you add something to the enum you were matching, it chokes. This is good.
- Move by default. If you came from c++, I think this makes a lot of sense. If you have a new language, don't bring the baggage.
- Easy way to use libraries. For now it hasn't splintered into several ways to build yet, I think most people still use cargo. But cargo also seems to work nicely, and it means you don't spend a couple of days learning cmake.
- Better error handling. There's a few large firms that don't use exceptions in c++. New language with no legacy? Use the Ok/Error/Some/None thing.
- Immutable by default. It's better to have everything locked down and have to explicitly allow mutation than just have everything mutable. You pay every time you forget to write mut, but that's pretty minor.
- Testing is part of the code, doesn't seem tacked on like it does in c++.
> Match needs to be exhaustive.
When I see people mention C++ with MISRA rules, I just think -- why do we need all these extra rules, often checked by a separate static analysis tool and enforced manually (that comes down to audit/compliance requirement), when they make perfect sense and could be done by the compiler? Missing switch cases happens often when an enum value is modified to include one extra entry and people don't update all code that uses it. Making it mandatory at compiler level is an obvious choice.
-Wswitch ΒΆ
Warn whenever a switch statement has an index of enumerated type and lacks a case for one or more of the named codes of that enumeration. (The presence of a default label prevents this warning.) case labels that do not correspond to enumerators also provoke warnings when this option is used, unless the enumeration is marked with the flag_enum attribute. This warning is enabled by -Wall.
<https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#inde...>The compiler can do that... And it's included in -Wall. It's not on by default but is effectively on in any codebase where anyone cares...
Please don't argue about "but I don't need to add a flag in Rust" it's not rust, there's reasons the standard committee finds valid for why and honestly your welcome to implement your own compiler that turns it on by default just like the rust compiler which has no standard because "the compiler is the standard".
MISRA won't be OK with that.
MISRA requires that you explicitly write the default reject. So -Wswitch doesn't get it done even though I agree that if C had (which it did not) standardized this requirement that would get you what you need.
C also lacks Rust's non_exhaustive trait. If the person making a published Goose type says it's non-exhaustive then in their code nothing changes, all their code needs to account for all the values of type Goose as before - but everybody else using that type must accept that the author said it's non-exhaustive, so they cannot account for all values of this type except by writing a default handler.
So e.g if I publish an AmericanPublicHoliday type when Rust 1.0 ships in 2015, and I mark it non-exhaustive since by definition new holidays may be added, you can't write code to just handle each of the holidays separately, you must have a default handler. When I add Juneteenth to the type, your code is fine, that's a holiday you must handle with your default handler, which you were obliged to write.
On the other hand IPAddr, the IP address, is an ordinary exhaustive type, if you handle both IPv6Addr and IPv4Addr you've got a complete handling of IPAddr.
"MISRA requires that you explicitly write the default reject."
You can always use -Wswitch-enum then.
Ugh too late to catch myself, non_exhaustive is an attribute, not a trait.
> Please don't argue about "but I don't need to add a flag in Rust"
Why not? It's a big issue. You say it's "on in any codebase where anyone cares", and I agree with that but in my experience most C++ developers don't care.
I regularly have to work with other people's C++ where they don't have -Wall -Werror. It's never an issue in Rust.
Also I don't buy that they couldn't fix this because it would be a breaking change. That's just an excuse for not bothering. They've made backwards incompatible changes in the past, e.g. removing checked exceptions, changing `auto`, changing the behaviour around operator==. They can just use the standard version, just like Rust uses Editions.
Of course they won't, because the C++ standards committee is still very much "we don't need seatbelts, just drive well like me".
> I regularly have to work with other people's C++ where they don't have -Wall -Werror.
To be fair, -Werror is kind of terrible. The set of warnings is very sensitive to the compiler version, so as soon as people work on the project with more than one compiler or even more than one version of the same compiler, it just becomes really impractical.
An acceptable compromise can be that -Werror is enabled in CI, but it really shouldn't be the default at least in open-source projects.
A common trope that is probably ignored or even unknown to uninformed C/C++ programmers, is that -Werr should be used for debug builds (as you use during development) and never for release builds (as otherwise it will most probably break compilation in future releases of the compiler)
> A common trope that is probably ignored or even unknown to uninformed C/C++ programmers, is that -Werr (...)
Not even that. -Wall -Werror should be limited to local builds, and should never touch any build config that is invoked by any pipeline.
No you definitely want to enforce this in CI.
> No you definitely want to enforce this in CI.
No, not really. It makes absolutely no sense to block builds for irrelevant things as passing unused arguments to a function.