Cannot assign immutable field


#1

I have a struct and a vector to hold instances of that struct. When I add an instance to the struct and then access it with last and try to change the value of a field, I get an error.

struct Foo {
    bar: i32,
}

fn main() {
    let mut foos = Vec::new();
    let f = Foo{ bar: 10 };
    foos.push(f);

    let mut ff = foos.last().unwrap();
    ff.bar = 1;
}
src/main.rs:14:5: 14:15 error: cannot assign to immutable field `ff.bar`
src/main.rs:14     ff.bar = 1;

What confuses me is that both the vector and the item returned by last are mutable. I’ve read the field level mutability page in the documentation, but that says the following:

The mutability of a struct is in its binding

In the code above, my bindings are all mut. What am I missing?


#2

That’s slightly simplified. Immutable references breaks that rule, and that’s the problem in this case. .last() will return an immutable reference to the last element, meaning that you can’t change it unless it’s internally mutable, but that’s an other story. What you want is .last_mut().


#3

ff is mut &Foo. You can reassign ff with another immutable reference, but cannot change borrowed object.

let ff = foos.last_mut().unwrap();

In the example above ff is &mut Foo. You cannot reassign ff, but can change borrowed object.


#4

What effect does mut have in the line let mut ff = foos.last().unwrap(); then? What exactly is mutable?


#5

Only ff in this case. You can replace the reference with an other:

let mut ff = foos.last().unwrap();
ff = &foos[0];

#6

I see. That’s a very subtle difference. Thank you.


#7

Yeah, an addition to the general rule is that immutable references has to be treated as a unit, meaning that you can only replace it as a whole.


#8

Is that why, if I try to get the length of the vector (when using last_mut), that it fails to compile? I’m not just borrowing the item returned but the entire vector?


#9

Yes, the method borrows the vector and the borrow is then “extended” (kind of) through the reference to the item.