I found that when I wrap a struct with ManuallyDrop, I can not borrow two fields from this struct as mutable at the same time. When I remove the ManuallyDrop, every thing goes well. Also, The error does not happen If I use a Box to wrap the struct instead of ManuallyDrop.
What's the reason behind this? Is there something special with ManuallyDrop?
My code is attached below. Thank you for your answer in advance.
use std::mem::ManuallyDrop;
fn main() {
struct A {
a: i32,
b: i32,
}
let mut a = ManuallyDrop::new(A{a:0, b:0}); // failed
// let mut a = Box::new(A{a:0, b:0}); // success
// let mut a = A{a:0, b:0}; // success
let aa = &mut a.a;
let ab = &mut a.b;
println!("{} {}", aa, ab);
}
/*
Compiler output:
Line 14, Char 19: cannot borrow `a` as mutable more than once at a time (solution.rs)
|
13 | let aa = &mut a.a;
| - first mutable borrow occurs here
14 | let ab = &mut a.b;
| ^ second mutable borrow occurs here
15 |
16 | println!("{} {}", aa, ab);
| -- first borrow later used here
For more information about this error, try `rustc --explain E0499`.
error: could not compile `prog` due to previous error
*.
It's because the field access goes through the DerefMut trait. Your code is short-hand for this:
use std::ops::DerefMut;
use std::mem::ManuallyDrop;
fn main() {
struct A {
a: i32,
b: i32,
}
let mut a = ManuallyDrop::new(A{a:0, b:0}); // failed
// let mut a = Box::new(A{a:0, b:0}); // success
// let mut a = A{a:0, b:0}; // success
let aa = &mut DerefMut::deref_mut(&mut a).a;
let ab = &mut DerefMut::deref_mut(&mut a).b;
println!("{} {}", aa, ab);
}
To fix it, you want this instead:
let a_ref = &mut *a;
let aa = &mut a_ref.a;
let ab = &mut a_ref.b;
which is short-hand for
let a_ref = DerefMut::deref_mut(&mut a);
let aa = &mut a_ref.a;
let ab = &mut a_ref.b;
Here, the type of a_ref is &mut A.
You will get the same error if you replace ManuallyDrop with any other wrapper that uses DerefMut to access the inner value, except for Box because Box is special.
Why would you want more types to be special? If anything, this is an argument for making Box less of a special-case.
Yes. Maybe removing special behaviors from Box is also an option. But in this case, I think what Box did is more convenient for us. Making all objects with DerefMut act the same as Box looks better.