rustreferencemutableborrow

Mutate an object which was taken from a vector


I have trouble solving an obvious simple problem.

Basically I want to push an instance of a structure to a vector to get it out later and to modify the object by calling a function implemented for the structure.

To simplify the case, I created the following test code, which reflects in my opinion to the same problem.

    let mut strings = Vec::new();
    strings.push("Hello".to_string());
    let string_option = strings.last();
    let string = string_option.unwrap();
    string.shrink_to(1);

This has the compile error

error[E0596]: cannot borrow `*string` as mutable, as it is behind a `&` reference
  --> src/main.rs:89:5
   |
88 |     let string = string_option.unwrap();
   |         ------ help: consider changing this to be a mutable reference: `&mut String`
89 |     string.shrink_to(1);
   |     ^^^^^^^^^^^^^^^^^^^ `string` is a `&` reference, so the data it refers to cannot be borrowed as mutable

Then I tried sheer endless variants like

    let mut strings = Vec::new();
    strings.push("Hello".to_string());
    let string_option = strings.last().as_mut();
    let string = string_option.unwrap();
    string.shrink_to(1);

... or ...

    let mut strings = Vec::new();
    strings.push("Hello".to_string());
    let string_option = strings.last().as_deref_mut();
    let string = string_option.unwrap();
    string.shrink_to(1);

Actually the code shown above is a simplification from this code, which I originally wanted to do.

struct Bar {
    data: Vec<String>
}

impl Bar {
    fn shrink_first(&mut self) {

        let s_opt = self.data.last().as_mut();  // s_opt is of type Option<&mut &String>

        let s = s_opt.unwrap(); // s is of type &mut & String
        s.shrink_to(1);
    }
}

The code above brings the following errors ...

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:67:21
   |
67 |         let s_opt = self.data.last().as_mut();  // s_opt is of type Option<&mut &String>
   |                     ^^^^^^^^^^^^^^^^         - temporary value is freed at the end of this statement
   |                     |
   |                     creates a temporary which is freed while still in use
68 |
69 |         let s = s_opt.unwrap(); // s is of type &mut & String
   |                 ----- borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

error[E0596]: cannot borrow `**s` as mutable, as it is behind a `&` reference
  --> src/main.rs:70:9
   |
70 |         s.shrink_to(1);
   |         ^^^^^^^^^^^^^^ cannot borrow as mutable

But in my opinion it always has the same root causes, but I have not figured out what to do.


Solution

  • Simply change strings.last() to strings.last_mut().

    The last() method returns a standard (immutable) reference (to be more precise, Option<&T>).

    To be able to mutate the last String in the vector, you need to get a mutable reference via last_mut().