cognitive-engineering-lab / rust-book

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

Chapter 4, Final Quiz (ch04-02-references-sec3-safety.toml) #172

Closed sgbmyr closed 1 week ago

sgbmyr commented 3 months ago

URL to the section(s) of the book with this problem: https://rust-book.cs.brown.edu/ch04-02-references-and-borrowing.html

Description of the problem:

A previously closed issue is available with a similar question here: 148

For the program below, can you add more explanation why "let v2 = &v is not a candidate [for accessing invalid data]"?

let v = vec![1, 2, 3];
let v2 = &v;
give_and_take(&v, 4);
println!("{}", v2[0]);

Questions:

  1. In my understanding, v2 would still reference v, which resides on the stack and the reference itself is not invalidated. Hence, println!("{}", v2) would possibly still work fine. But as soon as data were accessed with, e.g., v2[0], the program would access invalid data, because the heap memory pointed to by v was invalidated. Here, I assume that Rust does an implicit de-referencing, because without line 3 give_and_take(&v, 4) the program prints 1. Did I get something conceptually wrong here?
  2. More generally observing, wouldn't all answers actually suffer from a double-free issue as Rust would free v once more after it was invalidated?

Suggested fix: Possibly add more clarifications to the existing explanations.

Webzeez commented 3 months ago

Git clone money B2T rusttom no Tim h lol '

Archsx commented 3 months ago

I'm new to rust too, but let me try to explain.

  1. here is the involving tool that is mentioned somewhere in this book.(https://cognitive-engineering-lab.github.io/aquascope) to help you understand Rust, copy the code below to the box, click the button Interpret
    
    fn main() {
    let v = vec![1, 2, 3];
    let v2 = &v;
    give_and_take(&v, 4);
    println!("{}", v2[0]);
    }

fn give_and_take(v: &Vec, n: i32) -> i32 { v.push(n); v.remove(0) }


and you can see that `v2` always point to the `v` on the stack, **but not the real data in the heap**. after we call the function `give_and_take`, yes it will re-allocate memory on the heap, but `v2` still point to the `v` on the stack. so `v2[0]` always find the right address.
![Screenshot from 2024-03-25 11-08-54](https://github.com/cognitive-engineering-lab/rust-book/assets/21698481/69f17826-428e-4204-ad40-72ef7874213e)

![Screenshot from 2024-03-25 11-10-35](https://github.com/cognitive-engineering-lab/rust-book/assets/21698481/31dcc4d0-a19c-4f84-ae9b-8db98ac2c0b6)

2. I think double-free issue means **two variable** owns the same memory , but not **one variable** points to different memory after reallocating .
block0xhash commented 3 months ago

explanation why "let v2 = &v is not a candidate [for accessing invalid data]"?

This is because v2 is a reference to the "whole" vector and NOT a reference to a single element of the vector v

If the data in v mutates (by pushing or popping), it's not a problem because v2 is always pointing to wherever v is pointing

however:

let v2 = &v[0] will be a candidate for accessing invalid data.

Pushing to an array can cause the data to be allocated to another memory space, in this case v2 is left referencing deallocated memory . causing undefind behavior onprintln!

https://rust-book.cs.brown.edu/ch08-01-vectors.html

image

willcrichton commented 1 week ago

The explanations provided by @Archsx and @block0xhash are correct.