Rust中的多个可变借用

4

我正在尝试使用Rust构建一个非常简单的基于栈的求值器。我希望用户能够在后面定义函数,因此我将所有运算符存储在一个HashMap中,其中闭包作为值。

use std::collections::HashMap;

pub type Value = i32;

pub struct Evaluator<'a> {
    stack: Vec<Value>,
    ops: HashMap<String, &'a dyn FnMut(&'a mut Vec<Value>)>,
}

impl<'a> Evaluator<'a> {
    pub fn new() -> Evaluator<'a> {
        let stack: Vec<Value> = vec![];
        let mut ops: HashMap<String, &'a dyn FnMut(&'a mut Vec<Value>)> = HashMap::new();

        ops.insert("+".to_string(), &|stack: &'a mut Vec<Value>| {
            if let (Some(x), Some(y)) = (stack.pop(), stack.pop()) {
                stack.push(y + x);
            }
        });

        Evaluator { stack, ops }
    }

    pub fn stack(&self) -> &[Value] {
        &self.stack
    }

    pub fn eval(&'a mut self, input: &str) {
        let symbols = input
            .split_ascii_whitespace()
            .collect::<Vec<_>>();
        for sym in symbols {
            if let Ok(n) = sym.parse::<i32>() {
                self.stack.push(n);
            } else {
                let s = sym.to_ascii_lowercase();
                if let Some(f) = self.ops.get(&s) {
                    f(&mut self.stack);
                } else {
                    println!("error");
                }
            }
        }
    }
}

fn main() {
    let mut e = Evaluator::new();
    e.eval("1 2 +")
}

我目前遇到两个错误:

error[E0499]: cannot borrow `self.stack` as mutable more than once at a time
  --> src/sample.rs:34:17
   |
10 | impl<'a> Evaluator<'a> {
   |      -- lifetime `'a` defined here
...
34 |                 self.stack.push(n);
   |                 ^^^^^^^^^^ second mutable borrow occurs here
...
38 |                     f(&mut self.stack);
   |                     ------------------
   |                     | |
   |                     | first mutable borrow occurs here
   |                     argument requires that `self.stack` is borrowed for `'a`

error[E0596]: cannot borrow `**f` as mutable, as it is behind a `&` reference
  --> src/sample.rs:38:21
   |
38 |                     f(&mut self.stack);
   |                     ^ cannot borrow as mutable

error[E0499]: cannot borrow `self.stack` as mutable more than once at a time
  --> src/sample.rs:38:23
   |
10 | impl<'a> Evaluator<'a> {
   |      -- lifetime `'a` defined here
...
38 |                     f(&mut self.stack);
   |                     --^^^^^^^^^^^^^^^-
   |                     | |
   |                     | `self.stack` was mutably borrowed here in the previous iteration of the loop
   |                     argument requires that `self.stack` is borrowed for `'a`

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0499, E0596.
For more information about an error, try `rustc --explain E0499`.

我关心的是第一个问题。 我不确定我做错了什么,因为我没有同时借用它们。 我能告诉Rust之前的借用 (self.stack.pop()) 已经完成了吗? 非常感谢任何帮助。

2个回答

0

我想我解决了我的问题。我一直回到的问题是,“谁拥有这些闭包?”在这种情况下,我正在使用引用,但没有任何东西拥有数据所有权。当我重构(如下)并使用 Box 来拥有所有权时,它就有效了。

我很好奇是否有一种方法可以仅使用引用来完成此操作,或者我的解释是否错误?

工作代码:

use std::collections::HashMap;

pub type Value = i32;

pub struct Evaluator {
    stack: Vec<Value>,
    ops: HashMap<String, Box<dyn FnMut(&mut Vec<Value>)>>,
}

impl Evaluator {
    pub fn new() -> Evaluator {
        let stack: Vec<Value> = vec![];
        let mut ops: HashMap<String, Box<dyn FnMut(&mut Vec<Value>)>> = HashMap::new();

        ops.insert("+".to_string(), Box::new(|stack: &mut Vec<Value>| {
            if let (Some(x), Some(y)) = (stack.pop(), stack.pop()) {
                stack.push(y + x);
            }
        }));

        Evaluator { stack, ops }
    }

    pub fn stack(&self) -> &[Value] {
        &self.stack
    }

    pub fn eval(&mut self, input: &str) {
        let symbols = input
            .split_ascii_whitespace()
            .collect::<Vec<_>>();
        for sym in symbols {
            if let Ok(n) = sym.parse::<i32>() {
                self.stack.push(n);
            } else {
                let s = sym.to_ascii_lowercase();
                if let Some(f) = self.ops.get_mut(&s) {
                    f(&mut self.stack);
                } else {
                    println!("error");
                }
            }
        }
    }
}

fn main() {
    let mut e = Evaluator::new();
    e.eval("1 2 +")
}

0

你有冲突的生命周期借用:

  • 在第5行中为结构体定义了生命周期 'apub struct Evaluator<'a> {
  • 在第7行中,你声明ops是一个HashMap,它持有一系列在'a 生命周期内接收可变引用的函数
  • 然后,在第28行,你定义了一个eval方法,它在整个结构体('a)的生命周期内持有self的可变引用。

如果你使用两个不同的生命周期,就可以解决冲突,因为操作借用self的时间应该本质上比整个评估的生命周期更短,因为在eval中你正在运行循环和多次调用操作。

以下修改可以解决上述问题:

pub struct Evaluator<'a, 'b> {
    stack: Vec<Value>,
    ops: HashMap<String, &'b dyn FnMut(&'b mut Vec<Value>)>,
}

impl<'a, 'b> Evaluator<'a, 'b> {
    pub fn new() -> Evaluator<'a> {
        let stack: Vec<Value> = vec![];
        let mut ops: HashMap<String, &'b dyn FnMut(&'b mut Vec<Value>)> = HashMap::new();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接