Reputation: 1527
There are some similar questions, but the answers require the field to implement Default
or some way to initialize another value with the type of the field.
We have a Node
which has a value
of type T
:
struct Node<T> {
value: T,
next: Option<Box<T>>
}
It has a method which moves value
from a Node
:
impl<T> Node<T> {
fn value(self) -> T {
self.value
}
}
The code above compiles. But if we implement Drop
for Node
:
impl<T> Drop for Node<T> {
fn drop(&mut self) {}
}
Then we will get a compile error:
error[E0509]: cannot move out of type `Node<T>`, which implements the `Drop` trait
| self.value
| ^^^^^^^^^^
| |
| cannot move out of here
| move occurs because `self.value` has type `T`, which does not implement the `Copy` trait
I guess it doesn't compile because if we implement a custom Drop
, we need to make sure not to drop value
field if the drop happens at the end of value
method's block. However, we cannot check that; and even if we could, the compiler cannot statically check that we do it.
One way to workaround this is to store value
field as Option<T>
. But suppose that we don't want to use Option
for some reasons (the overhead, etc),
What else can we do to have both a custom Drop
and a value
method that moves value
field?
I guess we have to use some unsafe
approach and that's fine.
Upvotes: 4
Views: 425
Reputation: 1447
I don't know of a way to do this without using unsafe
(though someone else might), but here is a way to do it using unsafe
:
use std::{ptr, mem};
impl<T> Node<T> {
fn value(mut self) -> T {
unsafe {
let v: T = ptr::read(&self.value);
ptr::drop_in_place(&mut self.next);
mem::forget(self);
v
}
}
}
We use ptr::read
to move the desired value out. We then need to use mem::forget
on the Node
to make sure its drop
method is not called (since otherwise value
could be dropped twice and cause undefined behavior). To prevent the next
member from leaking, we use ptr::drop_in_place
to run its drop
method.
It's interesting to me that this safe code does not work:
impl<T> Node<T> {
fn value(self) -> T {
match self {
Node {value, next: _} => value
}
}
}
It gives that same error:
error[E0509]: cannot move out of type
Node<T>
, which implements theDrop
trait
I would have expected that with the match
expression taking ownership of all of self
and breaking it into its components, there would be no way for drop
to be called on self
and hence no reason for the compiler to complain. But apparently it does not work this way.
Upvotes: 1