Vector, immutable borrow error

I am still at the very beginning of learning.

I understand why example 8-7 from the book doesn't work (after adding element to vector there is no guaranty it will fit into memory, so whole vector might be moved and reference to the first element would be pointing to deallocated memory):

let mut v = vec![1, 2, 3, 4, 5];
let first = &v[0];
v.push(6);
println!("The first element is: {}", first);

But why only changing value of the element of known size (i32) doesn't work as well? There should be no change in memory. (I would understand this error when element is for example string)

   let mut numbers = vec![2, 4, 6, 8];
   let first = &numbers[0];
   numbers[1] = 3;
   println!("The first element is: {}", first);

thank you very much for clarification

It is because to set something to i-th element of vector, you need to mutate it (technicaly: you need a mutable borrow).

In expression: numbers[1] = 3; the only think that borrow checker understand is "numbers variable is mutated somehow". On the other hand, it knows, that this variable is borrowed (first keeps a borrow). Borrow checker knows, that if something is borrowed somewhere, it may not be mutated in any way, because something may rely on the state.

Your code is desugering to something like:

let mut numbers = vec![2, 4, 6, 8];
let first = std::ops::Index::index(&numbers, 0);
std::ops::IndexMut::index_mut(&mut numbers, 1) = 3;
println!("The first element is: {}", first);

So you see, that there is actually a mutable reference taken from numbers - borrow checker has no idea about underlying vector implementation - it just sees borrows, and has rules:

  1. If there is any shared borrow to variable, it can't be borrowed mutably
  2. If there is any mutable borrow to variable, it can't be borrowed at all

I suggest go through whole book once, and then go back to topics you still don't understand, so you can investigate them deeper.

1 Like

The type checker doesn't have enough information to know that the mutation on the third line can't invalidate the reference on the first line.

There are similar cases where the compiler is able to see that two accesses to a structure are disjoint. For example, you can mutate one field of a struct or tuple while a different field is borrowed:

let mut pair = (1, 2);
let first = &pair.0;
pair.1 = 3;
println!("The first element is {}", first);

But in general, safe Rust uses simple rules that prevent all "bad" programs from compiling but also prevent some "good" programs from compiling. Safe Rust only allows code that the compiler can prove obeys its aliasing rules. The compiler isn't sophisticated enough to prove this for all programs.

2 Likes

Thank you very much, I need to digest it slowly :slight_smile:
(and well, already now I am returning to various parts of the book, Rust is much more complex than I thought)

Thank you, now I understand the general logic behind.

I only hope, I don't need to understand all details of Rust like this

to be able to write something useful in this language...

Respect to all of you :+1:

No, you don't need to understand this details. You will probably get there in some time, but in general it is not needed to develop. The key is to understand, that the whole numbers is borrowed in both places, not single elements - thats why I desugared this code.

2 Likes