Почему метод Drop
имеет подпись fn drop(&mut self)
вместо fn drop(self)
? Это затрудняет перемещение значений из полей, например. self.join_handle.join()
или std::mem::drop(self.file)
(ошибка: не может выйти из типа X
, который определяет черту Drop
).
Почему Drop принимает & mut self вместо себя?
Ответ 1
Посмотрим, как реализована std::mem::drop
:
pub fn drop<T>(_x: T) { }
Правильно: это пустая функция! Это потому, что он использует семантику перемещения, чтобы получить право собственности на свой аргумент. Если T
реализует Drop
, компилятор автоматически вставляет вызов в Drop::drop(_x)
в конце функции. Это происходит со всеми аргументами, полученными значением (то есть, фактически, всеми аргументами, но удаление ссылки не отбрасывает референт).
Теперь рассмотрим, что произойдет, если Drop::drop
перенесет свой аргумент по значению: компилятор попытается вызвать Drop::drop
в аргументе внутри Drop::drop
— это вызовет переполнение стека! И, конечно, вы могли бы вызвать mem::drop
в аргументе, который также попытался бы рекурсивно вызвать Drop::drop
.
Ответ 2
Собственно, для Drop::drop
нет необходимости владеть значением.
В Rust собственность автоматически обрабатывается на уровне языка, и поэтому компиляция гарантирует правильную реализацию семантики владения; таким образом, когда a Foo { a: int, b: String }
выходит за пределы области видимости, компилятор отбрасывает Foo
, автоматически отбрасывая свои внутренние поля.
Таким образом, не нужно Drop::drop
отбрасывать поля!
На самом деле, после того, как Drop::drop
вызывается на Foo
, компилятор сам будет mem::drop
разными полями (которые также могут вызывать Drop::drop
для тех полей, которые его определяют, например b: String
здесь).
Что делает Drop::drop
, то?
Он используется для реализации дополнительной логики поверх того, что сделает компилятор; взяв пример JoinHandle
:
#[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) }
}
}
}
Здесь Drop::drop
используется для отсоединения потока, например.
В коллекции, такой как 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)
}
}
}
}
Здесь, когда необработанная память управляется способом, непрозрачным для компилятора, эта реализация заботится:
- Удаление каждого элемента, удерживаемого вектором
- Освобождение памяти