Thoughts on Rust
I've been using Rust for a month or so now, and I have some thoughts about it.
Diehard Rustaceans who believe their language is perfect in every way may wanna pass on this one.
Not everything I'm about to say is exactly nice, and I know how bigoted about Rust some of y'all are, and I know that any criticism of Ferris's Chosen Language™ is often swiftly met with rebukes and cries about memory safety and this and that.
Please understand, my criticism is not designed to undermine Rust! My criticism comes from a place of actually caring about the language. I want to see my gripes with it get fixed.
With all that out of the way...
The Good™
Rust does get a lot of things right.
- Generics are awesome, as are generic traits, GAT's, and such
- Traits rock; the ability to just extend any type is great
- Love it or hate it, the borrowing mechanism is helpful in ensuring I never have to worry about use-after-free (unless I drop to unsafe... more on that in a moment)
- Unsafe is a great escape hatch when you need it
- The C FFI is pretty good
- Default immutability makes you think about how you mutate data and side effects in your code, which is a good thing
- Dependencies and packaging are mostly done right with Cargo, at least, better than I've seen most programming languages (looking at you, Python)
- There is just enough access control to be useful, and do most useful tasks (like
pub(super)
,pub(crate)
,pub
, and the default of private); private being the default makes you think about what you export - Results > exceptions, full stop, this is how C++ should have done it
- Clippy is one of the best static analysis tools I've ever used, bar none
- rustc has great diagnostics, probably the best of any language I've ever used
- Though some complain about it, I love the
?
operator - The lack of function overloading; 99% of the time, when I wish I had overloading, what I really want is a generic
- I actually like
dyn
a lot, the way it does trait-based polymorphism feels very natural to me - Concurrency in Rust is pretty good for the most part
- The ability to easily export things to the C ABI is great
- Having code work about as fast as the equivalent C/C++ code without much effort whilst being safe is fantastic
rustdoc
is Fucking Awesome!- Rust encourages test-driven development
The Bad™
There's a lot I don't like, though.
- Rust is a very complex language, more complex than C, and that's saying something (although it isn't a fractal of complexity like C++)
- Why oh why do we need to explicitly upcast integers, the type system should not shit itself when an upcast is needed
- Is your string a
String
,str
,[u8]
,Vec<u8>
,CString
,OsString
? - Conversion between string types is not straightforward
.as_str()
vsto_str()
... make up your fucking minds, which is it???- Interop with C often requires silly casts and is quite noisy
.into()
either works or it doesn't, and you'll be left wondering why- If you need to build your own copy of the standard library (e.g. with
opt-level = "z"
), you need nightly panic()
should not unwind... why does it unwind wtf?- The C++ FFI story is pure 100% shit
- Too much boilerplate for simple tasks like error handling
- A lot of good stuff is in nightly only, with stabilisation seemingly not a priority
- GUI is a horror show
- Although not as prevalent as it once was, a lot of people are strapped to nightly at the hip, so you might be too
str.len()
is the number of bytes in the string, which is rarely a useful thing to know,str.size()
should be the number of bytes instead- Unicode manipulation in Rust is a sad story and you still need external libs; sad because they literally got it right by making strings UTF-8 by default, but gave you very few tools to do anything useful with them
- Unless you use nightly, you have to repeat yourself with array type sizes:
let arr[u8; 3] = [0u8; 3];
no_std
sucks, a lot, it's borderline unusable outside of embedded and with support libs- OOM conditions always just abort, which may not be what you want
- No default, optional, or keyword arguments
- No native varargs
- C varargs are somehow still unstable, so you can't write a C standard library on stable
- You can't just turn an
OsString
into aCString
, which to me sorta defeats the point of it char
is a scalar value, not a codepoint or character- If you need to add a lifetime to a
struct
, you then need to modify everything that touches thatstruct
, includingimpl
s - As I've posted about before: no container traits
macro_rules!
are ugly and complex, proc macros are even worse- The standard library is a little too minimalist, and depending on third-party crates isn't always fun or even feasible (makes auditing painful)
- Lack of thread-safe data structures
- Sometimes you'll randomly find certain things are bottlenecks that you don't expect
- The borrow checker isn't always smart and doesn't always know when a borrow is safe
- rustc's static analysis could be improved
- Clippy should by default emit warnings about using
unwrap()
willy-nilly without a docstring - One real implementation means that the language is chained at the hip to LLVM's whims (yes I know about rustgcc, I look forward to being able to use it in 5-8 years)
- In general, Rust may be at 1.70 at the time of this writing, but it feels like a language still in beta
- Loading shared libraries at runtime is sadness and pain
- The Rust governance structure is opaque
- Sometimes what I want is the fastest integer data type on my architecture and don't care much about width because I'm not dealing with big numbers; closest you get is
std::ffi::c_int
, but that's still not quite right and unsatisfactory
The Ugly™
This stuff is really, truly, 100% unacceptable.
- The Rust Evangelism Strike Force® and the attitude that Thou Shalt Rewrite Everything In Rust for questionable benefits; there is room for many languages in this world
- The Rust community likes to pretend it invented programming safety; it did not, Lisp has been doing it since 1959
- The Rust community gives me “Haskell before they looked in the mirror and did something about their toxicity” vibes, they pretend their shit doesn't stink, but trust me... it does
- Editions are pointless, given you often still wind up depending on a specific rustc version or above
- Unsafe code in Rust is more painful than it needs to be; lots of spooky undefined behaviour around it
- If you write a lot of unsafe code (like in a kernel or such), Rust is honestly less ergonomic than C in many ways
- Interior mutability is painful
- Pointer provenance rules are largely undefined
- Pointers in general need an overhaul
- Stacked borrows with Miri are incredibly complex math-heavy things and aren't really how the language works, trying to understand it has given me migraines
- Lack of cyclic garbage-collected structures makes implementing things like doubly-linked lists and trees that refer to their parents an absolute irritant without dropping to unsafe; I do know about rust-gc, but it's just not as good at something built-in
- Speaking of that, I should not have to drop to unsafe to implement a linked list or tree
- The standard library's linked lists suck, you can't even insert in the middle in O(1) time, without using cursors which are somehow still not stable
- Rust's evolution is basically corporate-driven, not community-driven
- SO MANY FUCKING CRYPTO GRIFTER COMPANIES IN THE RUST COMMUNITY, burn them, burn them all to the fucking ground
Conclusion
I still love Rust. I could write a huge list about things I love and hate in C, too. However, my Gods, it could be so much better than it is.
I want to see it get better over time.
I doubt I'm the first to have noticed all these good, bad, and ugly things, certainly.
If you have a constructive argument for or against anything here, let me know what you think on Fedi. I welcome all constructive feedback. 💜
— Elizabeth Ashford (Elizafox) Fedi (elsewhere): @Elizafox@social.treehouse.systems Tip jar: PayPal || CashApp || LiberaPay