cognitive-engineering-lab / rust-book

The Rust Programming Language: Experimental Edition
https://rust-book.cs.brown.edu
Other
504 stars 82 forks source link

on "Understanding Ownership" #106

Open captkuykuy opened 11 months ago

captkuykuy commented 11 months ago

URL to the section(s) of the book with this problem:

https://rust-book.cs.brown.edu/ch04-01-what-is-ownership.html

Description of the problem:

the last quiz, question 4. it says this is NOT undefined behaviour:

let b = Box::new(0); let b2 = b; println!("{}", b); move_a_box(b2);

why? "b" is assigned and then used at a function call to println. therefore, "b" already looses ownership.

also, in a smartphone, quiz #4 submit button is not showing.

Suggested fix:

MisterDerpie commented 11 months ago

Currently reading through that chapter myself. First and foremost, as the authors state

Below are four snippets which are rejected by the Rust compiler. Imagine that Rust instead allowed these snippets to compile and run. Select each snippet that would cause undefined behavior

I also selected

let b = Box::new(0);
let b2 = b;
println!("{}", b);
move_a_box(b2);

And got quite confused why this was wrong. To my understanding, it's a bit on the verge of "Assume Rust allows this to be compiled". This allows the Rust compiler to for a moment ignore that we may access an address that isn't owned anymore, which generally isn't guaranteed to be safe, but for that snippet, it is.

Although ownership has changed at this point in time, two key things happen:

  1. b's reference is still valid, i.e. it still points to an address in the heap where 0 is written in, hence println! will not read undefined memory
  2. At any point in time, the memory address 0 is written to will only be attempted to be freed exactly once. When trying to free a memory address that isn't assigned anymore it results in undefined behavior, e.g. freeing it twice, which is not the case here.

To explain the second point, what happens is line-by-line, assuming 0xF is where the value is stored:

  1. 0xF contains 0, and b is pointing to 0xF, and b is the owner of 0xF
  2. 0xF contains 0, and b is pointing to 0xF, and b2 is pointing to 0xF, and b2 is the owner of 0xF
  3. println!("{}", b); results in println!("{}", 0xF); since b still points to 0xF, b2 is the owner of 0xF
  4. b2 loses ownership to move_a_box, now neither b or b2 own 0xF anymore

Thus, Rust attempts to free 0xF exactly once, and that is when the frame of move_a_box ends.

Note: This is my mental model of how Rust works so far, hence my attempt is also from a non-experienced Rust point of view.

willcrichton commented 11 months ago

@MisterDerpie's answer is spot-on. This program is rejected by the Rust compiler, but would not cause undefined behavior because the data pointed-to by b is not deallocated until after move_a_box has been called.

flanger001 commented 10 months ago

I think the quiz the OP mentions has a similar assumption encoded into it as the one in "Boxes Live in the Heap". The program would compile in that quiz, but we are asked about a which would not be accessible to us at that point.

Both of these quizzes ask the reader to reason about abstract states Rust would not actually allow to exist, and that is somewhat of a difficult charge in the context of "learning Rust". I fully understand this chapter is the inflection point in Rust's learning difficulty curve. I would propose that a quiz question could be tagged as "You cannot actually do this in Rust, but imagine you can" (perhaps "Abstract"?).

Ironically, typing this all out helped me accept the answer and this book's pedagogical style, but I feel like others coming here will eventually run into similar issues, so I am committing to leaving this comment anyway.

CybCom commented 2 months ago

Finally I count the question as "imagine the rust compiler does not use ownership to limit heap access, but still use ownership to free memory." This works, but still weird😂 All in all I don't think this is a good example as a quiz. Add an explanation of the word "imagine" as before would improve it.