Почему Drop принимает & mut self вместо себя?

Почему метод Drop имеет подпись fn drop(&mut self) вместо fn drop(self)? Это затрудняет перемещение значений из полей, например. self.join_handle.join() или std::mem::drop(self.file) (ошибка: не может выйти из типа X, который определяет черту Drop).

Ответ 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)
            }
        }
    }
}

Здесь, когда необработанная память управляется способом, непрозрачным для компилятора, эта реализация заботится:

  • Удаление каждого элемента, удерживаемого вектором
  • Освобождение памяти