不能在一个代码中一次多次借用可变性 - 但可以在另一个代码中非常相似

dar*_*que 8 rust

我的这个片段没有通过借阅检查器:

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),而且FunctionCopy).

改变&'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,它通过了借用检查程序并正常运行.

Vla*_*eev 7

您只需要删除不必要的生命周期限定符,以便编译代码:

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.


oli*_*obk 5

您已经陷入了终身陷阱。将相同的生存期添加到更多引用中,将使您的程序受到更多限制。增加更多的寿命并为每个参考提供最小的可能寿命将允许更多程序。正如@ 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)

始终尝试不使用任何生存期,并让编译器变得更聪明。如果失败了,请不要在所有地方浪费生命周期,请考虑要在哪里传递所有权以及在哪里限制引用的生命周期。