我的这个片段没有通过借阅检查器:
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() {}
Run Code Online (Sandbox Code Playgroud)
error[E0499]: cannot borrow `self.call_stack` as mutable more than once at a time
--> src/main.rs:29:9
|
27 | let fun = try!(self.get_function(fun_name));
| ---- first mutable borrow occurs here
28 |
29 | self.call_stack.push(fun);
| ^^^^^^^^^^^^^^^ second mutable borrow occurs here
...
32 | }
| - first borrow ends here
Run Code Online (Sandbox Code Playgroud)
我的直觉是,问题与HashMap返回None数据结构中的值或者值的引用有关.但我不希望这样:我的意图是self.get_function应该返回存储值的字节副本或错误(这就是我放的原因.map(|f| *f),而且Function是Copy).
改变&'a mut self别的东西没有用.
但是,接受以下片段,在精神上有些相似:
#[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);
}
Run Code Online (Sandbox Code Playgroud)
现在我很困惑.第一个片段有什么问题?为什么不在第二次发生呢?
这些代码段是更大代码段的一部分.为了提供更多的上下文,在Rust Playground上显示了一个更完整的示例,其中包含错误的代码,这显示了没有的早期版本HashMap,它通过了借用检查程序并正常运行.
您只需要删除不必要的生命周期限定符,以便编译代码:
fn get_function(&mut self, fun_name: &str) -> Result<Function<'a>, Error> { ... }
fn call(&mut self, fun_name: &str) -> Result<(), Error> { ... }
Run Code Online (Sandbox Code Playgroud)
你的问题是你把&mut self它存在的值的生命周期和生命周期联系起来(Function<'a>),这在大多数情况下是不必要的.由于get_function()定义中存在此依赖关系,编译器必须假定调用的结果self.get_function(...)借用self,因此它禁止您再次借用它.
&str参数的生命周期也是不必要的 - 它只是无缘无故地限制了可能的参数值集.您的密钥可以是具有任意生命周期的字符串,而不仅仅是'a.
您已经陷入了终身陷阱。将相同的生存期添加到更多引用中,将使您的程序受到更多限制。增加更多的寿命并为每个参考提供最小的可能寿命将允许更多程序。正如@ o11c指出的那样,消除'a生命周期的约束将解决您的问题。
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(())
}
}
Run Code Online (Sandbox Code Playgroud)
起作用的原因是Rust插入了新的生存期,因此在编译器中,函数的签名将如下所示:
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>
Run Code Online (Sandbox Code Playgroud)
始终尝试不使用任何生存期,并让编译器变得更聪明。如果失败了,请不要在所有地方浪费生命周期,请考虑要在哪里传递所有权以及在哪里限制引用的生命周期。