1

The below rust code has compilation error.

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

Unlike the question Cannot borrow `x` as mutable more than once at a time, add does not return any reference at all.

Unlike the question Borrow errors for multiple borrows, the return type is i32 which has 'static lifetime.

While the following code can be compiled without errors.

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

Anyway it is cumbersome to create a dummy variable x every time for a compound expression.

wcy
  • 875
  • 11
  • 12
  • In this specific case, `self` does not need to be `mut`. – Jmb Feb 16 '22 at 07:20
  • 1
    Does this answer your question [Why is there a borrow of a moved value when calling a method that takes self by value with an argument that also calls a method?](https://stackoverflow.com/q/55117392/2189130) *"Evaluation order, for the purpose of borrows, is from left to right"* - you have to split it up – kmdreko Feb 16 '22 at 07:27
  • @jmb I just simplify my question, in my real code, `builder` holds a counter and increase the counter after dispatching a unique id. – wcy Feb 16 '22 at 07:36
  • @kmdreko thanks, the question is quite similar to mine. – wcy Feb 16 '22 at 07:39

2 Answers2

1

First there will be a mutable borrow on the outer b.add(...) and then there will be an attempt to make a second mutable borrow on the inner b.add(). That is wonderfully explained & visualized by the compiler in the error message that you pasted:

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

This limitation might be removed at some point, but for now, that's how it works.

And for your specific example, as Jmb mentioned, you don't need mut at all, so this will work just fine:

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

fn main() {
    let b = Builder;
    b.add(b.add(10));
}
at54321
  • 8,726
  • 26
  • 46
1

It looks like you are attempting to use the builder pattern.

The difficulty stands in passing a &mut on the builder from call to call. Each step in the chain should rather consume the previous state of the builder and emit a new state (like add() here). Finally, the last step (build()) consumes the builder for the last time and emits instead the value we intended to build.

You might be worried about the performance cost of these multiple move/consume operations (instead of altering through a &mut), but when enabling optimizations, the compiler is able to look through these calls and can decide to do everything in place: a function only containing Builder::new().add(10).add(20).build() produces 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);
}
prog-fh
  • 13,492
  • 1
  • 15
  • 30