Most active commenters
  • throwawaymaths(3)

←back to thread

451 points birdculture | 16 comments | | HN request time: 0.483s | source | bottom
Show context
Animats ◴[] No.43979394[source]
It's like reading "A Discipline of Programming", by Dijkstra. That morality play approach was needed back then, because nobody knew how to think about this stuff.

Most explanations of ownership in Rust are far too wordy. See [1]. The core concepts are mostly there, but hidden under all the examples.

    - Each data object in Rust has exactly one owner.
      - Ownership can be transferred in ways that preserve the one-owner rule.
      - If you need multiple ownership, the real owner has to be a reference-counted cell. 
        Those cells can be cloned (duplicated.)
      - If the owner goes away, so do the things it owns.

    - You can borrow access to a data object using a reference. 
      - There's a big distinction between owning and referencing.
      - References can be passed around and stored, but cannot outlive the object.
        (That would be a "dangling pointer" error).
      - This is strictly enforced at compile time by the borrow checker.
That explains the model. Once that's understood, all the details can be tied back to those rules.

[1] https://doc.rust-lang.org/book/ch04-01-what-is-ownership.htm...

replies(17): >>43979460 #>>43979907 #>>43980199 #>>43981064 #>>43981313 #>>43981587 #>>43981720 #>>43982074 #>>43982249 #>>43982619 #>>43982747 #>>43983156 #>>43984730 #>>43988460 #>>43990363 #>>43996196 #>>44008391 #
1. ameliaquining ◴[] No.43979460[source]
Summarizing a set of concepts in a way that feels correct and complete to someone who understands them, is a much easier task than explaining them to someone who doesn't. If we put this in front of someone who's only worked with call-by-sharing languages, do you think they'll get it right away? I'm skeptical.
replies(4): >>43979483 #>>43979677 #>>43979870 #>>43981052 #
2. Animats ◴[] No.43979483[source]
Right. If you come to Rust from C++ and can write good C++ code, you see this as "oh, that's how to think about ownership". Because you have to have a mental model of ownership to get C/C++ code to work.

But if you come from Javascript or Python or Go, where all this is automated, it's very strange.

3. bloppe ◴[] No.43979677[source]
For me it really clicked when I realized ownership / lifetimes / references are just words used to talk about when things get dropped. Maybe because I have a background in C so I'm used to manual memory management. Rust basically just calls 'free' for you the moment something goes out of scope.

All the jargon definitely distracted me from grasping that simple core concept.

replies(3): >>43979804 #>>43979871 #>>43980449 #
4. math_dandy ◴[] No.43979870[source]
The list in the above comment isn’t a summary — it’s a precise definition. It can and must be carefully explained with lots of examples, contrasts with other languages, etc., but the precise definition itself must figure prominently, and examples and intuition should relate back to it transparently.
5. josephg ◴[] No.43979871[source]
Almost all of it.

Rust also has the “single mutable reference” rule. If you have a mutable reference to a variable, you can be sure nobody else has one at the same time. (And the value itself won’t be mutated).

Mechanically, every variable can be in one of 3 modes:

1. Directly editable (x = 5)

2. Have a single mutable reference (let y = &mut x)

3. Have an arbitrary number of immutable references (let y = &x; let z = &x).

The compiler can always tell which mode any particular variable is in, so it can prove you aren’t violating this constraint.

If you think in terms of C, the “single mutable reference” rule is rust’s way to make sure it can slap noalias on every variable in your program.

This is something that would be great to see in rust IDEs. Wherever my cursor is, it’d be nice to color code all variables in scope based on what mode they’re in at that point in time.

replies(1): >>43987139 #
6. mikepurvis ◴[] No.43980449[source]
"Rust basically just calls 'free' for you the moment something goes out of scope."

C++ does that too with RAII. Go ahead and use whatever STL containers you like, emplace objects onto them, and everything will be safely single-owned with you never having to manually new or delete any of it.

The difference is that C++'s guarantees in this regard derive from a) a bunch of implementation magic that exists to hide the fact that those supposedly stack-allocated containers are in fact allocating heap objects behind your back, and b) you cooperating with the restrictions given in the API docs, agreeing not to hold pointers to the member objects or do weird things with casting. You can use scoped_ptr/unique_ptr but the whole time you'll be painfully aware of how it's been bolted onto the language later and whenever you want you can call get() on it for the "raw" underlying pointer and use it to shoot yourself in the foot.

Rust formalizes this protection and puts it into the compiler so that you're prevented from doing it "wrong".

replies(2): >>43981314 #>>43982628 #
7. andrewflnr ◴[] No.43981052[source]
Practically, I think it suggests that learning the borrow checker should start with learning how memory works, rather than any concepts specific to Rust.
8. throwawaymaths ◴[] No.43981314{3}[source]
the tradeoff is that ~you have to guess where rust is doing the frees, and you might be wrong. in the end this would be strictly equivalent to an explicit instruction to free, with the compiler refusing to compile if the free location broke the rules.

It's really too bad rust went the RAII route.

replies(3): >>43982274 #>>43982685 #>>43982728 #
9. whyever ◴[] No.43982274{4}[source]
How often do you care about the order in which objects are dropped?
replies(1): >>43984059 #
10. ShroudedNight ◴[] No.43982628{3}[source]
> a bunch of implementation magic that exists to hide the fact that those supposedly stack-allocated containers are in fact allocating heap objects behind your back

The heap is but one source for allocator-backed memory. I've used pieces of stack for this, too. One could also use an entirely staticly sized and allocated array.

11. swiftcoder ◴[] No.43982685{4}[source]
There's no guessing - Rust has well defined drop order. It also has manual drop, should you wish to override the defined order.
replies(1): >>43984049 #
12. imtringued ◴[] No.43982728{4}[source]
Okay, but an IDE could just visualize that for you. A linter rule could force you to manually drop if you want to be explicit.
13. throwawaymaths ◴[] No.43984049{5}[source]
sorry i shouldn't have said guess. i meant consider
14. throwawaymaths ◴[] No.43984059{5}[source]
anything where you need to have stuff run in constant time.
replies(1): >>43986537 #
15. mikepurvis ◴[] No.43986537{6}[source]
If you're in hard realtime land then you can't have any allocations at all; that's a pretty different ballgame.

Destructors should be as simple and side-effect free as possible, with the exception of things like locks or file handles where the expectation is very clear that the object going out of scope will trigger a release action.

16. bloppe ◴[] No.43987139{3}[source]
Ya, I just think the `mut` thing isn't as much of a challenge to newcomers as the memory management aspect.