在没有引用返回值的情况下,一次不能多次借用“b”作为可变的 [duplicate]

Cannot borrow `b` as mutable more than once at a time without reference return value [duplicate]

提问人:wcy 提问时间:2/16/2022 更新时间:2/16/2022 访问量:1412

问:

下面的 rust 代码有编译错误。

struct Builder;
impl Builder {
    pub fn add(&mut self, id: i32) -> i32 {
        id + 1
    }
}

fn main() {
    let mut b = Builder;
    b.add(b.add(10));
}
error[E0499]: cannot borrow `b` as mutable more than once at a time
  --> examples/mutable_borrow_at_a_time.rs:10:11
   |
10 |     b.add(b.add(10));
   |     - --- ^ second mutable borrow occurs here
   |     | |
   |     | first borrow later used by call
   |     first mutable borrow occurs here

问题不同,一次不能多次借用“x”作为可变的,根本不返回任何引用。add

与问题“多次借入的借入错误”不同,返回类型是具有生存期的返回类型。i32'static

而以下代码可以编译而不会出错。

struct Builder;
impl Builder {
    pub fn add(&mut self, id: i32) -> i32 {
        id + 1
    }
}

fn main() {
    let mut b = Builder;
    let x = b.add(10);
    b.add(x);
}

无论如何,每次为复合表达式创建一个虚拟变量都很麻烦。x

编译器错误 可变 借用检查器

评论

0赞 Jmb 2/16/2022
在这种特定情况下,不需要 .selfmut
1赞 kmdreko 2/16/2022
这是否回答了您的问题:为什么在调用一个按值获取自变量的方法时,会借用一个移动值,而参数也调用一个方法? “评估顺序,出于借用的目的,是从左到右”——你必须把它拆分
0赞 wcy 2/16/2022
@jmb我只是简化了我的问题,在我的真实代码中,持有一个计数器并在调度唯一 ID 后增加计数器。builder
0赞 wcy 2/16/2022
@kmdreko谢谢,这个问题与我的问题非常相似。

答:

1赞 at54321 2/16/2022 #1

首先,在外部会有一个可变的借用,然后会尝试在内部进行第二个可变的借用。编译器在您粘贴的错误消息中对此进行了精彩的解释和可视化:b.add(...)b.add()

|     b.add(b.add(10));
|     - --- ^ second mutable borrow occurs here
|     | |
|     | first borrow later used by call
|     first mutable borrow occurs here

此限制可能会在某个时候删除,但就目前而言,这就是它的工作原理。

对于您的特定示例,正如 Jmb 所提到的,您根本不需要,因此这将正常工作:mut

struct Builder;
impl Builder {
    pub fn add(&self, id: i32) -> i32 {
        id + 1
    }
}

fn main() {
    let b = Builder;
    b.add(b.add(10));
}
1赞 prog-fh 2/16/2022 #2

看起来您正在尝试使用构建器模式

困难在于将构建器从一个呼叫传递到另一个呼叫。 链中的每个步骤都应该消耗构建器的先前状态并发出一个新状态(如下所示)。 最后,最后一步 () 最后一次使用构建器,并发出我们打算构建的值。&mutadd()build()

您可能会担心这些多个移动/使用操作的性能成本(而不是通过 更改),但是在启用优化时,编译器能够查看这些调用,并可以决定执行所有操作:一个仅包含 produce 的函数。&mutBuilder::new().add(10).add(20).build()mov eax, 30 ret

struct Builder {
    id: i32,
}

impl Builder {
    pub fn new() -> Self {
        Builder { id: 0 }
    }

    pub fn add(
        mut self,
        incr: i32,
    ) -> Self {
        self.id += incr;
        self
    }

    pub fn build(self) -> i32 {
        self.id
    }
}

fn main() {
    let id = Builder::new().add(10).add(20).build();
    println!("id={:?}", id);
}