Не может заимствовать как изменяемый более чем один раз за один код - но может в другом очень похоже

У меня есть этот фрагмент, который не проходит проверку заимствования:

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() {}
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

Я чувствую, что проблема связана с тем, что 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);
}

Теперь я в замешательстве. В чем проблема с первым фрагментом? Почему это не происходит во втором?

Фрагменты кода являются частью большего куска кода. Чтобы предоставить больше контекста, это на Rust Playground показывает более полный пример с неисправным кодом, и это показывает более раннюю версию безHashMap, который проходит проверку и работает нормально.

2 ответа

Решение

Вы попали в пожизненную ловушку. Добавление того же времени жизни к большему количеству ссылок еще больше ограничит вашу программу. Добавление большего количества жизней и предоставление каждой ссылки минимально возможного времени жизни позволит больше программ. Как отмечает @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(())
    }
}

Это работает потому, что 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>

Всегда старайтесь не использовать время жизни и пусть компилятор будет умным. Если это не помогло, не распыляйте время жизни везде, подумайте, где вы хотите передать право собственности и где вы хотите ограничить срок жизни ссылки.

Вам нужно только удалить ненужные пожизненные квалификаторы, чтобы ваш код компилировался:

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

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

Ваша проблема заключалась в том, что вы связали жизнь &mut self и время жизни значения, хранящегося в нем (Function<'a>), что в большинстве случаев не нужно. С этой зависимостью, которая присутствовала в get_function() определение, компилятор должен был предположить, что результат вызова self.get_function(...) заимствует selfи, следовательно, он запрещает вам заимствовать его снова.

Время жизни на &str Аргумент также не нужен - он просто ограничивает возможный набор значений аргумента без причины. Ваш ключ может быть строкой с произвольным временем жизни, а не только 'a,

Другие вопросы по тегам