Cannot borrow `*self` as mutable because it is also borrowed as immutable


#1

Can anyone explain to me why this error occurs?
Below I wrote a simple code sample that does not work.
I’m new to the Rust and I do not understand much about this yet. I will be very grateful to you if you can explain to me the reason of this error.

use std::ops::Index;

#[derive(Debug)]
struct TestChild<'a> {
    name: &'a str,
}

impl<'a> TestChild<'a> {
    fn new(name: &'a str) -> Self {
        Self { name }
    }
}

#[derive(Debug)]
struct Parent<'a> {
    children: Vec<TestChild<'a>>,
}

impl<'a> Parent<'a> {
    fn new() -> Self {
        let init = TestChild::new("Init child");
        Self {
            children: vec![init],
        }
    }

    fn add_child(&mut self, child: TestChild<'a>) {
        self.children.push(child);
        println!("Parent {:?}", self);
    }

    fn get_lat(&self) -> &TestChild<'a> {
        self.children.index(self.children.len() - 1)
    }

    fn test(&mut self, name: &'a str) {
        let last = self.get_lat();
        println!("Last child is {:?}", last);
        let child = TestChild::new(name);
        self.add_child(child);
    }
}

fn main() {
    let mut p = Parent::new();
    p.test("Foo");
    p.test("Bar");
}

#2

self.get_lat() borrows a value from &self, and that will restrict what else you can do with self until that borrow is released – when your last goes out of scope at the end of the function.

If the compiler allowed you to call add_child, this could invalidate the immutable reference you have. It doesn’t actually do inter-function analysis to decide whether anything really is invalidated – just the fact that the method takes &mut self is all it cares about. In your case the clear problem would be if push had to reallocate the vector, then all existing values would move.

Since you don’t need last that long, you can give it a scoped block like this:

    fn test(&mut self, name: &'a str) {
        {
            let last = self.get_lat();
            println!("Last child is {:?}", last);
        }
        let child = TestChild::new(name);
        self.add_child(child);
    }

Or just print it directly so the borrow is temporary:

        println!("Last child is {:?}", self.get_lat());

When we get non-lexical lifetimes (now on nightly with #![feature(nll)]), the compiler will automatically reduce the borrowed lifetime to the last place your last binding is used, so your original code would just work as-is.


#3

If you’re new to Rust — don’t use references in structs. They’re not pointers. In almost all basic cases you need to use owned types that can be stored and freely moved around, rather than temporary references/locks borrowed from somewhere else. In your case use String not &str.