#Motivation
struct Foo<'r> {
i: Box<Baz>,
b: Bar<'r>,
}
struct Baz;
struct Bar<'r> {
i: &'r Baz,
}
let boxed_baz = Box::new(Baz{});
let boxed_ref: &Baz = boxed_baz.as_ref();
let f = Foo{i: boxed_baz, b: Bar{i: boxed_ref}};
Which results in:
error[E0505]: cannot move out of `boxed_baz` because it is borrowed
--> src/main.rs:51:20
|
50 | let boxed_ref: &Baz = boxed_baz.as_ref();
| --------- borrow of `boxed_baz` occurs here
51 | let f = Foo{i: boxed_baz, b: Bar{i: boxed_ref}};
| ^^^^^^^^^ move out of `boxed_baz` occurs here
A more real example would look like this
struct Device;
struct CommandBuffer {
device: &Device,
// ...
}
// Does not work because Rust does not allow internal pointers
struct Something {
device: Device,
command_buffer: CommandBuffer,
}
The workaround for this problem is to fallback to Arc
struct Device;
struct CommandBuffer {
device: Arc<Device>,
// ...
}
struct Something {
device: Arc<Device>,
command_buffer: CommandBuffer,
}
Arc imposes the following drawback
- Atomic counter
- Ignores Rust’s lifetime system
#Solution
Add a builtin trait to allow moves even with active borrows.
unsafe impl<T> BorrowMove<&T> for Box<T> {}
If Rust encounters a borrow of type &T
that is linked to a Box<T>
, it will allow the box to be moved to a equal or larger lifetime.
Rust will still refuse to move Box<T>
for every other active borrow, this includes &Box<T>
.
#Problems
Unsure how this will interact with custom alloactors.
#Closing thoughts
This is not really a well crated RFC. I encountered the problem recently while designing vulkan wrapper.
The proposed solution is probably too optmistic and will have some problems. The main goal for the pre-rfc is to see if moving should be allowed for some types that have active borrows.