3

My erroneous code snippet and compiler error info:

// code snippet 1: 0 fn main() { 1 let mut x: Box<i32> = Box::new(4); 2 let r: &Box<i32> = &x; 3 *x = 8; 4 println!("{}", r); 5 } 
// compiler error info: error[E0506]: cannot assign to `*x` because it is borrowed --> src/main.rs:3:4 | 2 | let r = &x; | -- borrow of `*x` occurs here 3 | *x = 8; | ^^^^^^ assignment to borrowed `*x` occurs here 4 | println!("{}", r); | - borrow later used here For more information about this error, try `rustc --explain E0506`. 

The following code won't compile, which makes quite senses to me cause we cannot invalidate the reference r .

// code snippet 2: 0 fn main() { 1 let mut x: i32 = 0; 2 let r: &i32 = &x; 3 x = 1; 4 println!("{}", r); 5 } 

But the compiler error info of code snippet1 doesn't make too much sense to me.

x is a pointer on the stack pointing to a heap memory segment whose contents is 4 , reference r only borrows x (the pointer not the heap memory segment) , and in line 3 *x = 8; , what we did here is to alter the memory on the heap (not the pointer on the stack) . Change happens on the heap , while reference is only relevant to the stack, they do not interrelate.

This question is kind of picking a quarrel, but I do not mean to argue for the sake of argument.

If you found my question irregular, feel free to point it out :)

4 Answers 4

4

Change happens on the heap , while reference is only relevant to the stack, they do not interrelate.

That does not matter, because the type system doesn't work with that "depth" of information.

As far as it's concerned, borrowing x is borrowing the entirety of x up to any depth, and so any change anywhere inside x is forbidden.

For type checking purposes, this is no different than if x were a Box<Vec<_>>, and r were be actively used for iteration, leading any update to the inner vector to possibly invalidate the iterator.

(also type-wise *x = 8 does require first taking a unique reference to the box itself, before "upgrading" it to a unique reference to the box' content, as you can see from the trait implementation)

Sign up to request clarification or add additional context in comments.

Comments

4

Rust's entire borrowing model enforces one simple requirement: the contents of a memory location can only be mutated if there is only one pointer through which that location can be accessed.

In your case, the heap location that you're trying to mutate can be accessed both through x and through r—and therefore mutation is denied.

This model enables the compiler to perform aggressive optimisations that permit, for example, the storage of values reachable through either alias in registers and/or caches without needing to fetch again from memory when the value is read.

Comments

2

The semantics of * is determined by two traits:

pub trait Deref { type Target: ?Sized; fn deref(&self) -> &Self::Target; } 

or

pub trait DerefMut: Deref { fn deref_mut(&mut self) -> &mut Self::Target; } 

In your case, when you write *x = 8 Rust compiler expands the expression into the call DerefMut::deref_mut(&mut x), because Box<T> implements Deref<Target=T> and DerefMut. That is why in the line *x = 8 mutable borrowing of x is performed, and by orphan rule it can't be done, because we've already borrowed x in let r: &Box<i32> = &x;.

Comments

0

I just found a great diagram from Programming Rust (Version 2), which really answers my question quite well: enter image description here

In the case of my question, when x is shared-referenced by r, everything in the ownership tree of x (the stack pointer and the heap memory segment) becomes read-only.

I knew that the Stack Overflow community does not like pictures, but this diagram is really great and may help someone who will find this question in the future:)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.