Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

rust - Mutable borrow in function argument

Why doesn't the following code compile (playground):

use std::collections::HashMap;

fn main() {
    let mut h: HashMap<u32, u32> = HashMap::new();
    h.insert(0, 0);
    h.insert(1, h.remove(&0).unwrap());
}

The borrow checker complains that:

error[E0499]: cannot borrow `h` as mutable more than once at a time
 --> src/main.rs:6:17
  |
6 |     h.insert(1, h.remove(&0).unwrap());
  |     - ------    ^ second mutable borrow occurs here
  |     | |
  |     | first borrow later used by call
  |     first mutable borrow occurs here

The code is safe, however, and an almost mechanical transformation of the last line makes it compile (playground):

    //h.insert(1, h.remove(&0).unwrap());
    let x = h.remove(&0).unwrap();
    h.insert(1, x);

It was my understanding that this kind of issue got resolved with non-lexical lifetimes. This question is an example, and there are many others.

Is there some subtlety that makes the first variant incorrect after all, so Rust is correct to refuse it? Or is the NLL feature still not finished in all cases?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Your question also applies for a related case that may be more surprising — having the method call require &self when the method argument requires &mut self:

use std::collections::HashMap;

fn main() {
    let mut h: HashMap<u32, u32> = HashMap::new();
    h.insert(0, 0);
    h.contains_key(&h.remove(&0).unwrap());
}

The Rust borrow checker uses what it calls two-phase borrows. An edited transcription of a chat I had with Niko Matsakis:

The idea of two-phase borrows is that the outer &mut is treated like an & borrow until it is actually used, more or less. This makes it compatible with an inner & because two & mix, but it is not compatible with an inner &mut.

If we wanted to support that, we'd have had to add a new kind of borrow -- i.e., an "unactivated" &mut wouldn't act like an &, it would act like something else (&const, maybe... "somebody else can mutate")

It's less clear that this is OK and it seemed to add more concepts so we opted not to support it.

As you stated, this is safe because the inner borrow is completed before the outer borrow starts, but actually recognizing that in the compiler is overly complex at this point in time.

See also:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

2.1m questions

2.1m answers

60 comments

57.0k users

...