Cannot borrow as mutable more than once at a time in one code - but can in another very similar

I have this snippet that fails check validation:

use std::collections::HashMap; enum Error { FunctionNotFound } #[derive(Copy, Clone)] struct Function<'a> { name: &'a str, code: &'a [u32] } struct Context<'a> { program: HashMap<&'a str, Function<'a>>, call_stack: Vec<Function<'a>>, } impl<'a> Context<'a> { fn get_function(&'a mut self, fun_name: &'a str) -> Result<Function<'a>, Error> { self.program.get(fun_name).map(|f| *f).ok_or(Error::FunctionNotFound) } fn call(&'a mut self, fun_name: &'a str) -> Result<(), Error> { let fun = try!(self.get_function(fun_name)); self.call_stack.push(fun); Ok(()) } } fn main() { } 

I feel the problem is that the HashMap returns either None or a reference to the value inside the data structure. But I do not want this: my intention is that self.get_function should return either a byte copy of the stored value or an error (so I set .map(|f| *f) , and Function - Copy ).

Changing &'a mut self to something else does not help.

However, the following passage, somewhat similar in spirit, was adopted:

 #[derive(Debug)] enum Error { StackUnderflow } struct Context { stack: Vec<u32> } impl Context { fn pop(&mut self) -> Result<u32, Error> { self.stack.pop().ok_or(Error::StackUnderflow) } fn add(&mut self) -> Result<(), Error> { let a = try!(self.pop()); let b = try!(self.pop()); self.stack.push(a + b); Ok(()) } } fn main() { let mut a = Context { stack: vec![1, 2] }; a.add().unwrap(); println!("{:?}", a.stack); } 

So now I'm embarrassed. What is the problem with the first fragment? (and why doesn't this happen in the second?)

Excerpts are part of a larger code. To provide more context, this on Rust Playground shows a more complete example with faulty code, and this shows an earlier version without a HashMap that passes the borrower check and works fine.

+6
source share
2 answers

You are trapped in life. Adding the same lifetime to more links will more limit your program. Adding more lives and providing each link with the shortest possible lifetime will allow more programs. As @ o11c notes, eliminating life time limits 'a will solve your problem.

 impl<'a> Context<'a> { fn get_function(&mut self, fun_name: &str) -> Result<Function<'a>, Error> { self.program.get(fun_name).map(|f| *f).ok_or(Error::FunctionNotFound) } fn call(&mut self, fun_name: &str) -> Result<(), Error> { let fun = try!(self.get_function(fun_name)); self.call_stack.push(fun); Ok(()) } } 

The reason for this is that rust inserts new lifetimes, so in the compiler the signatures of your functions will look like this:

 fn get_function<'b>(&'b mut self, fun_name: &'b str) -> Result<Function<'a>, Error> fn call<'b>(&'b mut self, fun_name: &'b str) -> Result<(), Error> 

Always try not to use the lifetime and let the compiler be smart. If this fails, don’t spray your life time everywhere, think about where you want to transfer ownership and where you want to limit the lifetime of the link.

+4
source

You only need to remove the unnecessary time qualifiers so that your code can compile:

 fn get_function(&mut self, fun_name: &str) -> Result<Function<'a>, Error> { ... } fn call(&mut self, fun_name: &str) -> Result<(), Error> { ... } 

Your problem was that you linked the lifetime of &mut self and the lifetime of the value stored in it ( Function<'a> ), which in most cases is not necessary. With this dependency, which was present in the definition of get_function() , the compiler should have assumed that the result of calling self.get_function(...) is self , and therefore it forbids you to borrow it again.

The Lifetime on &str argument is also not needed - it simply limits the possible set of argument values ​​for no reason. Your key can be a string with an arbitrary lifetime, not just 'a .

+7
source

Source: https://habr.com/ru/post/989788/


All Articles