Why does Drop
’s method have signature fn drop(&mut self)
instead of fn drop(self)
? This makes it difficult to move values out of the fields e.g. self.join_handle.join()
or std::mem::drop(self.file)
(error: cannot move out of type X
, which defines the Drop
trait).
Typically brands will take 24-72 hours to process and ship orders, but shipping times will vary depending on the brand. The delivery will be sent in accordance to the rules of the site.
Come to find out shipping will take 4-8 weeks for an item listed as "in stock".
Orders are packaged and shipped usually within 3 business days, shipping times will vary based on the shipping method chosen.
Canada: 1 to 3 weeks from the date of shipping. Australia, New Zealand, Germany, France, and the UK: 2 to 4 weeks from the date of shipping. Other countries not listed above: 3 to 6 weeks from the date of shipping.
Let's look at how std::mem::drop
is implemented:
pub fn drop<T>(_x: T) { }
That's right: it's an empty function! That's because it takes advantage of move semantics to acquire ownership of its argument. If T
implements Drop
, the compiler automatically inserts a call to Drop::drop(_x)
at the end of the function. This happens to all arguments received by value (that is, in fact, all arguments whatsoever, but dropping a reference doesn't drop the referent).
Now consider what would happen if Drop::drop
took its argument by value: the compiler would try to invoke Drop::drop
on the argument within Drop::drop
— this would cause a stack overflow! And of course, you would be able to call mem::drop
on the argument, which would also try to recursively call Drop::drop
.
Actually, it is unnecessary for Drop::drop
to take ownership of the value.
In Rust, ownership is automatically handled at language level, and therefore the compiler makes sure to properly implement ownership semantics; thus when a Foo { a: int, b: String }
goes out of scope, the compiler will drop Foo
by dropping its inner fields automatically.
It is thus unnecessary for Drop::drop
to drop the fields!
Actually, after Drop::drop
is called on Foo
, the compiler will itself mem::drop
the different fields (which may also invoke Drop::drop
on those fields which define it, such as b: String
here).
What does Drop::drop
do, then?
It is used to implement extra logic on top of what the compiler will do; taking your JoinHandle
example:
#[stable(feature = "rust1", since = "1.0.0")]
#[unsafe_destructor]
impl<T> Drop for JoinHandle<T> {
fn drop(&mut self) {
if !self.0.joined {
unsafe { imp::detach(self.0.native) }
}
}
}
Here, Drop::drop
is used to detach the thread, for example.
In a collection such as Vec::vec
:
#[unsafe_destructor]
#[stable(feature = "rust1", since = "1.0.0")]
impl<T> Drop for Vec<T> {
fn drop(&mut self) {
// This is (and should always remain) a no-op if the fields are
// zeroed (when moving out, because of #[unsafe_no_drop_flag]).
if self.cap != 0 && self.cap != mem::POST_DROP_USIZE {
unsafe {
for x in &*self {
ptr::read(x);
}
dealloc(*self.ptr, self.cap)
}
}
}
}
Here, as the raw memory is manipulated in a way opaque to the compiler, this implementation takes care of:
I still needed clarification despite the good answers above. This is what I got...
The Drop trait,
pub trait Drop {
pub fn drop(&mut self);
}
requires a mutable reference
instead of a move
because the drop
function is not intended to release memory but merely perform preparatory work prior to that release. Consequently, a better name for that drop
method is perhaps, prepare_to_drop
.
The actual memory release is performed by a function in the std::mem
module that actually takes a self
as is defined as follows:
pub fn drop<T>(_x: T) { }
This function is plainly using the compiler lifetime management semantics, as expected, in taking ownership of T
and discarding it implicitly. A more revealing encoding of this particular function sheds light on the compiler's gymnastics, if we presume the compiler implicitly derives an empty Drop
trait for all types that do not implement their own...
//Note: this is a bare function (no Self parameter)
pub fn drop<T: Drop>(x: T) {
x.drop(); //prepare_to_drop
} //release memory since argument is owned
// and scope is at end.
For completeness, the reason the Drop
trait requires a mutable reference
, &mut T
, instead of a shared reference
, &T
, is that the preparatory work may include altering the contents of T
such as reassigning owned values or references by swapping out the value with a default value (see Option.take
), since mutations aren't permitted on shared references
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With