Mutable borrow more than once [duplicate]

2019-07-01 21:31发布

问题:

This question already has an answer here:

  • How to update-or-insert on a Vec? 2 answers

This is a short sample of short example that doesn't compile. The error is in the add1 function. It works if I do as in add2, but this isn't very DRY. Can someone with more experience enlighten me of how to overcome the mutable borrow more than once error in a better way than in add2.

struct S1 {
    full: bool,
    v: Vec<u32>,
}

struct S2 {
    v: Vec<S1>,
}

impl S2 {
    // If last is not full push n to last.v, otherwise push a new S1

    // Doesn't work
    fn add1(&mut self, n: u32) {
        // Let's assume it's not empty
        let s1 = self.v.last_mut().unwrap();
        if !s1.full {
            s1.v.push(n);
        } else {
            self.v.push(S1 {
                full: false,
                v: vec![n],
            });
        }
    }

    // Works
    fn add2(&mut self, n: u32) {
        // First getting last as immutable ref and then as mutable ref
        let full = self.v.last().unwrap().full;
        if !full {
            self.v.last_mut().unwrap().v.push(n);
        } else {
            self.v.push(S1 {
                full: false,
                v: vec![n],
            });
        }
    }
}

fn main() {}

playground

The compiler error:

error[E0499]: cannot borrow `self.v` as mutable more than once at a time
  --> src/main.rs:20:13
   |
16 |         let s1 = self.v.last_mut().unwrap();
   |                  ------ first mutable borrow occurs here
...
20 |             self.v.push(S1 {
   |             ^^^^^^ second mutable borrow occurs here
...
25 |     }
   |     - first borrow ends here

回答1:

You have two options.

1) Use nightly and put #![feature(nll)] at the top of your file.

Non-lexical lifetimes solve exactly this issue: even though the s1 borrow is not used in the else block, it's still alive and blocking mutation of self.v. With non-lexical lifetimes, the compiler recognizes that s1 is effectively dead and lets you borrow again.

2) Structure your code like this:

fn add1(&mut self, n: u32) {
    { // add a scope around s1 so that it disappears later
        let s1 = self.v.last_mut().unwrap();
        if !s1.full {
            s1.v.push(n);
            return; // just return early instead of using else
        }
    }
    self.v.push(S1 {
        full: false,
        v: vec![n]
    });
}


回答2:

You can limit the repetitions like this:

fn add1(&mut self, n: u32) {
    // Let's assume it's not empty
    if {
        let s1 = self.v.last_mut().unwrap();
        if !s1.full {
            s1.v.push(n);
        }
        s1.full
    } {
        self.v.push(S1 {
            full: false,
            v: vec![n],
        });
    }
}

playground



标签: rust