Janos
Janos

Reputation: 21

How to avoid borrowing twice?

I am having trouble wrapping my head around the borrowing going on in the example below. Both find_command_by_name and find_command_by_alias borrow &mut self. self refers to a CommandManager which contains commands as fields, so I understand that if I have some &mut Command field I cannot access its parent &mut self anymore since that would be violating having two mutable pointers to the same object.

However, what if cmd is None? In that case, there is no second mutable pointer, but the borrow checker is still telling me I cannot use &mut self anymore. How can I ensure that I am not borrowing twice, or tell the borrow checker that &mut self is actually safe to use?

Any design patterns to help with this sort of problem are appreciated.

fn find_command(&mut self, input : &Vec<String>) -> Option<&mut Command> {
    let mut cmd = self.find_command_by_name(input);
            
    if cmd.is_some() {
        return cmd
    }

    let cmd = self.find_command_by_alias(input); // Error - self is being borrowed twice!

    return cmd
}

Upvotes: 1

Views: 646

Answers (1)

cdhowie
cdhowie

Reputation: 169143

Indeed, this should be allowed. It is a longstanding bug.

There are a few workarounds, one of which is to forcibly unbind the reference from the lifetime of &mut self by using unsafe. Note that the lifetimes are still bound together from the perspective of the caller, so this doesn't create a safety issue for users of this function.

fn find_command<'a>(&'a mut self, input : &Vec<String>) -> Option<&'a mut Command> {
    let cmd = self.find_command_by_name(input);

    if cmd.is_some() {
        return cmd.map(|v| unsafe { &mut *(v as *mut _) });
    }

    let cmd = self.find_command_by_alias(input); // Error - self is being borrowed twice!

    cmd
}

Pointers don't have lifetimes associated with them, so casting the reference to a pointer and then back to a reference "disconnects" them. The cast-and-borrow is an opaque wall to the borrow checker.

Of course, this throws a bit of safety out the window, so you need to be careful when editing this code later. In particular, you need to be sure that you're giving a reference that's valid for 'a to the unsafe block.

Another workaround is safer, but repeats the first operation:

fn find_command<'a>(&'a mut self, input : &Vec<String>) -> Option<&'a mut Command> {
    match self.find_command_by_name(input) {
        Some(_) => self.find_command_by_name(input),
        None => self.find_command_by_alias(input)
    }
}

Upvotes: 2

Related Questions