Hacker News new | ask | show | jobs
by jcranmer 1262 days ago
From the standpoint of being productive in Rust, this is what you need to know:

* An object has a lifetime. This generally lasts until overwritten or destroyed at end of scope.

* You can loan out references to this object. You can either have a single &mut, xor an unlimited number of & references, but not both.

* References cannot outlast the lifetime of the object they point to.

* If you have a &mut reference to an object, you can give out another &mut reference to the same object. But you can't use the first reference for the duration of the second.

* Lifetimes can be named, and you can express criteria of the form "this lifetime must at least/most this long."

* Bonus: if you have &mut x, you can give out &mut x.a and &mut x.b at the same time. But you can't give out &mut x while such a subreference is live.

And... that's really all you need. Yeah, there's a lot more rules, and there's definitely fun edge cases around things like temporary objects' lifetimes. But there's no need to know all of that stuff. If you get things wrong, the compiler will come back and slap you in the face and give you an error message--that's the selling point of Rust, getting lifetimes wrong is an error, not silent nonsense--and your task at that point is to figure out if you're really breaking a cardinal rule (two or more mutable references to the same memory location, or references not lasting long enough), or if you didn't enforce sufficiently strict requirements for the bounds of the lifetimes. And the rust compiler is pretty good at telling you what you have to do to get necessary lifetime bounds (sometimes too good--it can suggest fixes to lifetime bounds even when such bounds are unachievable).

I'd compare this to things like name lookup and overload resolution in C++, which are actually horrendously confusing algorithms that almost nobody understands in their entirety. Yet plenty of people can be productive in C++ because the general principles are well-understood, and if you're at the point where you need to descend into the morass of exceptions and exceptions-to-exceptions, you're probably writing confusing code to begin with.

1 comments

But I've never heard a C++ programmer deny that it's a large and difficult language.

I personally never really had a problem with ownership model - probably because what I was doing fell into the simple buckets. Rust's difficulty goes well beyond ownership. I just thought the Rust user forum thread I quoted was a gently funny example of the Rust community's denial: "Rust's not hard, but to get a decent mental model of the borrow checker you need to read the standard library, or if not here's 10000 words on how I think of it". I'm not presenting it as "proof" that Rust is hard - the fact that I couldn't do anything practical with it after more troublesome attempts than with any other language is plenty enough for me to know that. Neither do I think difficulty is 'bad'. Denying it can be though.

That's someone who's defining "mental model" as effectively "build a formal model of how this things worth without actually using formalism for everything." Of course everyone in that thread is coming up with very complicated stuff, because OP explicitly asked them to do so.

Yes, Rust's borrower checker is more complicated than equivalent features in other languages. If you read the writings of the Rust language developers, you'll notice that they basically admit that it's where Rust sinks its entire complexity budget, so there's no room to spend it anywhere else. But I don't think it's too complex--it's not complex enough for average developers to not be productive. I would contrast this with C++'s template metaprogramming, which I believe to be too complex for average developers to be productive (e.g., trying to write templates that switch based on the types of the parameters, especially before if constexpr).