不能在一个代码中一次多次借用可变性 - 但可以在另一个非常相似的代码中 [英] Cannot borrow as mutable more than once at a time in one code - but can in another very similar

查看:71
本文介绍了不能在一个代码中一次多次借用可变性 - 但可以在另一个非常相似的代码中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've this snippet that doesn't pass the borrow checker:

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

My gut feeling is that the problem is tied to the fact that HashMap returns either None or a reference of the value inside the data structure. But I don't want that: my intention is that self.get_function should return either a byte copy of the stored value or an error (that's why I put .map(|f| *f), and Function is Copy).

&'a mut self 更改为其他东西无济于事.

Changing &'a mut self to something else doesn't help.

但是,下面的代码片段在精神上有些相似,可以接受:

However, the following snippet, somewhat similar in spirit, is accepted:

#[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);
}

现在我很困惑.第一个片段有什么问题?为什么不发生在第二个?

Now I'm confused. What is the problem with the first snippet? Why doesn't it happen in the second?

这些片段是较大代码段的一部分.为了提供更多上下文,这个在 Rust Playground 显示带有错误代码的更完整示例,这显示了没有 HashMap,通过借用检查器,正常运行.

The snippets are part of a larger piece of code. In order to provide more context, this on the Rust Playground shows a more complete example with the faulty code, and this shows an earlier version without HashMap, which passes the borrow checker and runs normally.

推荐答案

你掉进了生命周期的陷阱.将相同的生命周期添加到更多引用将更多地限制您的程序.添加更多生命周期并为每个引用赋予最小可能的生命周期将允许更多程序.正如@o11c 所指出的,删除对 'a 生命周期的约束将解决您的问题.

You have fallen into the lifetime-trap. Adding the same lifetime to more references will constrain your program more. Adding more lifetimes and giving each reference the minimal possible lifetime will permit more programs. As @o11c notes, removing the constraints to the 'a lifetime will solve your issue.

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 插入了新的生命周期,因此在编译器中,您的函数签名将如下所示:

The reason this works is that Rust inserts new lifetimes, so in the compiler your function's signatures will look like this:

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>

总是尽量不使用任何生命周期,让编译器变得聪明.如果失败了,不要到处都喷生命周期,想想你想在哪里传递所有权,以及你想在哪里限制引用的生命周期.

Always try to not use any lifetimes and let the compiler be smart. If that fails, don't spray lifetimes everywhere, think about where you want to pass ownership, and where you want to limit the lifetime of a reference.

这篇关于不能在一个代码中一次多次借用可变性 - 但可以在另一个非常相似的代码中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆