The signature of Drop::drop
is fn drop(&mut self);
, and the signature of std::mem::drop
is pub fn drop<T>(_x: T) {}
.
Why not change the signature of Drop::drop
to fn drop(self);
? Why the extra step? Is it related to panics?
Thanks
The signature of Drop::drop
is fn drop(&mut self);
, and the signature of std::mem::drop
is pub fn drop<T>(_x: T) {}
.
Why not change the signature of Drop::drop
to fn drop(self);
? Why the extra step? Is it related to panics?
Thanks
If drop
took the receiver by value, then it would be dropped again at the end of Drop::drop()
, resulting in infinite recursion, double frees, etc.
That makes sense, thanks!
About the mem::drop
vs Drop::drop
distinction: The former does more than just calling the latter: Calling mem::drop
works on types that implement Drop
and types that don’t and (in either case) it also drops all the fields, recursively, while Drop::drop
implementations do generally not handle dropping the fields and assume that that happens automatically afterwards.
Also note that mem::drop
is not magical or internal to the compiler or special in any way. In fact it’s simply an ordinary function defined as
pub fn drop<T>(_x: T) {}
The destruction happens because _x
goes out of scope in the end of this call.
See more in the reference
Destructors
When an initialized variable or temporary goes out of scope, its destructor is run, or it is dropped. Assignment also runs the destructor of its left-hand operand, if it's initialized. If a variable has been partially initialized, only its initialized fields are dropped.
The destructor of a type
T
consists of:
- If
T: Drop
, calling<T as std::ops::Drop>::drop
- Recursively running the destructor of all of its fields.
- The fields of a struct are dropped in declaration order.
- The fields of the active enum variant are dropped in declaration order.
- The fields of a tuple are dropped in order.
- The elements of an array or owned slice are dropped from the first element to the last.
- The variables that a closure captures by move are dropped in an unspecified order.
- Trait objects run the destructor of the underlying type.
- Other types don't result in any further drops.
If a destructor must be run manually, such as when implementing your own smart pointer,
std::ptr::drop_in_place
can be used.
Note that the drop_in_place
function mentioned above is pretty magical i.e. not an ordinary function you could write yourself. And it also demonstates another reason why &mut self
is useful for destructors: You can implement Drop
for unsized types, too, but you couldn’t directly pass unsized types by value.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.