Differences between assigning data in owned and borrowed objects


#1

I’m currently trying to learn the fundamental concepts of rust borrows. Currently I’m struggling with understanding the limitations of changing referenced value within a borrowed struct.


In Rust it is possible to have a immutable struct containing a mutable borrow. It is then possible to mutate the value of the inner reference, like in the following example:

struct C<'a> {
    value: &'a mut u32
}

fn main() {
    let mut x = 42;
    {
        let cell = C { value: &mut x };
        *cell.value = 99;
    }
    assert_eq!(x, 99);
}

Consequently, we can create a function which takes a C and changes the value of the reference.

fn set_to_99(cell: C) {
    *cell.value = 99;
}

...
{
    let cell = C { value: &mut x };
    set_to_99(cell);
}

However this moves the ownership of the object to that function and it therefore would be nice if we could use a borrow instead:

fn set_to_99(cell: &C){
    *cell.value = 99;
}

This though throws the error: cannot assign to data in a `&` reference [E0389]. Using mutable references in contrast do work.

fn set_to_99(cell: &mut C){
    *cell.value = 99;
}
...
{
    let mut cell = C{ value: &mut x };
    set_to_99(&mut cell);
}

Using a &mut enforces that the underlying object is also mutable. Thus the actual immutable object has to be declared as mutable to allow the borrow to change the inner value.


So this boils down to: Why is it allowed to assign data in an immutable object but not in a & reference?


#2

It’s also interesting that adding a mut doesn’t trigger the “variable does not need to be mutable” warning… implying that it needs to be mutable? :confused:

Without mut you can’t reassign the value field itself though, so that’s what immutability of the object means here:

fn main() {
    let mut x = 42;
    let mut y = 42;
    {
        let cell = C { value: &mut x };
        cell.value = &mut y; // cannot assign to immutable field `cell.value`
    }
}

It should be noted that when you own an object it’s not fundamentally immutable (because there’s still no aliasing like in the immutable & borrow case; ignoring interior mutability for simplicity), you’re temporarily prevented from mutating it but can rebind it as mutable at any time:

let mut cell = cell;

#3

Wow thanks, somehow I have missed that.


I’ve discussed the problem with a colleague and he pointed out the following:

fn main() {
    let mut x = 42;
    {
        let cell = C { value: &mut x };
        let ref1 = &cell;
        let ref2 = &cell;
    }
}

If &cell would preserve the &mut borrow, we suddenly would have two mutable references to the same object. Thus & somehow cancels out the mutability of the borrow.


#4

Sometimes it’s easier to use the ‘dual’ names for ‘mutable’ and ‘immutable’ borrows – ‘unique’ and ‘aliased’ respectively. Mutating x requires a unique borrow and while there is an aliased borrow let ref1 = &cell; you can’t make a unique borrow needed to assign *cell.value = 99. An owned variable despite it not being bound with mut satisfies the ‘unique’ requirement.