Reputation: 4231
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.
Upvotes: 2
Views: 93
Reputation: 430604
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 a a 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:
Upvotes: 3