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

I have seen this issue/topic is covered many times in forum but the context contains match in my case. First, let me present the method:

    fn invoke(&mut self, line: &str) -> ShellResult<()> {
        match line.find(' ') {
            Some(index) => {
                let name = &line[0..index];
                let args = &line[index + 1..];
                match self.codes.iter().find(|c| c.name == name) {
                    Some(code) => {
                        code.invokable.invoke(Box::new(self), args);
                        Ok(())
                    }
                    None => Err(ShellError::DoesNotExist {
                        name: name.to_owned(),
                    }),
                }
            }
            None => match self.codes.iter().find(|c| c.name == line) {
                Some(code) => {
                    code.invokable.invoke(Box::new(self), "");
                    Ok(())
                }
                None => Err(ShellError::DoesNotExist {
                    name: line.to_owned(),
                }),
            },
        }
    }

It fails saying:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
   --> src/lib.rs:121:56
    |
119 |                 match self.codes.iter().find(|c| c.name == name) {
    |                       ---------- immutable borrow occurs here
120 |                     Some(code) => {
121 |                         code.invokable.invoke(Box::new(self), args);
    |                                        ------          ^^^^ mutable borrow occurs here
    |                                        |
    |                                        immutable borrow later used by call

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
   --> src/lib.rs:131:52
    |
129 |             None => match self.codes.iter().find(|c| c.name == line) {
    |                           ---------- immutable borrow occurs here
130 |                 Some(code) => {
131 |                     code.invokable.invoke(Box::new(self), "");
    |                                    ------          ^^^^ mutable borrow occurs here
    |                                    |
    |                                    immutable borrow later used by call

So, as far as I see, it seems like Rust immutably borrows self for matching. I need to provide self to invoke method, the signature of which is:

fn invoke(&self, writable: Box<&mut dyn Write>, args: &str);

And I provide self as Boxed because I provide it as std::io::Write and it definitely implements that.

So, how do I provide self to invoke method in this case?


Environment

  • Rust 1.43.1

In this case, you need to restructure the code so that you can access the immutable data separately from Write implementation. I think the easiest way is usually to split the structure into two parts, one with the data for what invoke needs, and another for the Write implementation. Note that you don't necessarily need to change your existing struct, just add two new ones with the two parts - something like:

struct ShellWrite<'a> {
    val: &'a mut thingfrommainstruct,
}
struct InvokeData<'a> {
    val: &'a thingfrommainstruct,
}
impl MainStruct {
    fn split_for_invoke(&mut self) -> (InvokeData<'_>, ShellWrite<'_>) {
        // create both from fields and return
    }
}

In some situations, you can restructure the code so this isn't necessary. But for your snippet, it is, because as you've written it you do have aliasing mutable and immutable references to self.


As an example of why rust prevents this, consider these implementations of invoke and Write:

struct MainStruct {
    data: Vec<u8>,
}
impl MainStruct {
    fn invoke(&self, writable: Box<&mut dyn Write>, args: &str) {
        let thing_to_write = &self.data[0..4];
        writeable.write_all(thing_to_write);
        println!("just wrote {}!", thing_to_write);
    }
}
impl Write for MainStruct {
    fn write(&mut self, buf: &[u8]) {
        self.data.write(buf);
    }
}

The call to write_all changes self.data, and could cause the vec to reallocate - and this would invalidate thing_to_write. The println!() call would then be undefined behavior as it's reading from freshly deallocated memory.

Since the borrow checker operates purely on function signatures, for all it knows, you could have these implementations. So it can't allow code which gives mutable access to self at the same time as having separate immutable access.

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.