Multiple mutable borrows in Rust

Viewed 640

I'm playing around with building a very simple stack based evaluator in Rust. I want the user to be able to define functions later, so I'm storing all operators in a HashMap with closures as values.

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 +")
}

I'm currently getting two errors:

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`.

My concern is the first one. I'm not sure what I'm doing wrong as I'm not borrowing them at the same time. Can I tell Rust the previous borrow (self.stack.pop()) is done? Any help appreciated.

2 Answers

I think I solved my problem. The thing I kept coming back to is, "What owns the closures?" In this case I'm using references, but nothing is taking ownership of the data. When I refactored (below) with Box to take ownership, it worked.

I'm curious if there is a way to do this with with just references and/or if my explanation is wrong?

Working code:

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 +")
}

You have borrows with conflicting lifetimes:

  • You are defining a lifetime 'a for the struct in line 5: pub struct Evaluator<'a> {
  • In line 7, you are stating that ops is a HashMap that holds functions that receive mutable borrows for the whole duration of 'a
  • Then, in line 28, you are defining an eval method that holds a mutable reference to self for the whole duration of the struct ('a)

The conflict can be solved if you use two different lifetimes, since the time that an operation borrows self should be inherently shorter than the lifetime for the whole evaluation, since in eval you are running a loop and multiple invocations to the operations.

This should fix the issues mentioned above:

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();
Related