Need help with mutable and immutable borrow of *self

In the code below, calling a &mut self method while iterating a field of self will result in a immutable borrow and a mutable borrow. There will be two borrows and the compilation will fail.

#[derive(Default)]
struct Test {
    list: Vec<Vec<i32>>,
    cnt: Vec<i32>,
}

impl Test {
    pub fn func1(&mut self) {
        for i in self.list[0].iter() {  // immutable borrow of *self
            self.func2(*i);     // mutable borrow of *self
        }
    }

    pub fn func2(&mut self, i: i32) {}
}

fn main() {
    let mut test = Test::default();
    test.func1();
}

I can make a copy of the list vector and iterate over it, but is there a easier way to write this kind of code?

This is an interprocedural conflict; the article explores some general workaround strategies.

For the particular example, if you don't want to change the signatures and you don't need access to the list[0] during the call to func2, you could temporarily remove it from self:

    pub fn func1(&mut self) {
        let list0 = std::mem::take(&mut self.list[0]);
        for i in &list0 {
            self.func2(*i);
        }
        self.list[0] = list0;
    }

Playground. Docs for std::mem::take, with links to replace and swap which serve similar use cases.

1 Like

Hello, I'm just learning myself, but I'll try to help solve this one. One question first: why does func2 need a &mut self? Will it modify one of the vectors? I'm asking to find out whether you're wanting to do something that is unsafe.

func2 needs a &mut self because it will modify some of the fields of the object (for example cnt field).

OK, if func2 only needs to access cnt (and not list) then what @quinedot suggested would work.

Another possibility is to separate the cnt and list in two different structs so that exclusive ownership of one does not require exclusive ownership of the other; however, that may be inconvenient.

1 Like

Thanks, the link is quite helpful.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.