简体   繁体   中英

Why can I not borrow a variable as mutable more than once at a time with a &mut Box<T> while &mut T works?

I'm trying to implement a linked list in Rust and I'm having some trouble understanding the difference between these two functions:

enum List<T> {
    Nil,
    Cons(T, Box<List<T>>)
}

fn foo<T>(list: &mut Box<List<T>>) {
    match **list {
        List::Nil => return,
        List::Cons(ref mut head, ref mut tail) => {
            // ...
        }
    }
}

fn bar<T>(list: &mut List<T>) {
    match *list {
        List::Nil => return,
        List::Cons(ref mut head, ref mut tail) => {
            // ...
        }
    }
}

foo fails to compile, with the following error:

error[E0499]: cannot borrow `list` (via `list.1`) as mutable more than once at a time
  --> src/main.rs:66:34
   |
66 |         List::Cons(ref mut head, ref mut rest) => {
   |                    ------------  ^^^^^^^^^^^^ second mutable borrow occurs here (via `list.1`)
   |                    |
   |                    first mutable borrow occurs here (via `list.0`)
...
69 |     }
   |     - first borrow ends here

However, bar compiles and runs perfectly. Why does bar work, but not foo ? I am using Rust version 1.25.

This can be simplified to

fn foo(v: &mut Box<(i32, i32)>) {
    match **v {
        (ref mut head, ref mut tail) => {}
    }
}

or

fn foo(v: &mut Box<(i32, i32)>) {
    let (ref mut head, ref mut tail) = **v;
}

The problem is that Box is aa strange, in-between type.

Way back in Rust's history, Box was special-cased by the compiler; it knew a lot of the details of Box , but this meant that it was "magic" and no one else could implement something that worked like Box .

RFC 130 proposed changing that; making Box "just another type". Unfortunately, this still hasn't been fully transitioned.

The details are nuanced, but basically the current borrow checker handles pattern-matching syntactically , not semantically . It needs to do this to prevent some unsoundness issues.

In the future, non-lexical lifetimes (NLL) just magically fix this; you don't have to to anything (hooray!).

Until then, you can explicitly get back to a &mut T with this ugly blob:

match *&mut **list {

Or call DerefMut explicitly:

match *std::ops::DerefMut::deref_mut(list) {

However, there's very little reason to accept a &mut Box<T> .

See also:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM