Why is calling `Drop::drop` not allowed, requiring `mem::drop`?

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 :slight_smile:

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.

2 Likes

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:

  1. If T: Drop, calling <T as std::ops::Drop>::drop
  2. 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.

7 Likes

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.