Reputation: 21
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
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