Почему метод Drop
имеет подпись fn drop(&mut self)
вместо fn drop(self)
? Это затрудняет перемещение значений из полей, например. self.join_handle.join()
или std::mem::drop(self.file)
(ошибка: не может перемещаться из типа X
, который определяет черту Drop
).Почему Drop принимает & mut self вместо себя?
ответ
Давайте посмотрим на то, как std::mem::drop
реализуется:
pub fn drop<T>(_x: T) { }
Правильно: это пустая функция! Это потому, что он использует преимущества семантики перемещения, чтобы получить право собственности на свой аргумент. Если T
реализует Drop
, компилятор автоматически вставляет вызов в Drop::drop(_x)
в конце функции. Это происходит со всеми аргументами, полученными по значению (то есть, фактически, все аргументы, но сброс ссылки не отбрасывает референт).
Теперь рассмотрим, что произойдет, если Drop::drop
взял свой аргумент по значению: компилятор попытается вызвать Drop::drop
на аргумент в Drop::drop
— это может вызвать переполнение стека! И, конечно, вы могли бы вызвать mem::drop
по аргументу, который также попытался бы рекурсивно вызвать Drop::drop
.
На самом деле, для Drop::drop
нет необходимости владеть стоимостью.
В Rust владение автоматически обрабатывается на уровне языка, и поэтому компиляция гарантирует правильную реализацию семантики владения; таким образом, когда Foo { a: int, b: String }
выходит за пределы области видимости, компилятор отбрасывает Foo
, автоматически отбрасывая свои внутренние поля.
Таким образом, нет необходимости в Drop::drop
, чтобы сбросить все поля!
На самом деле, после того, как Drop::drop
вызывается Foo
, компилятор будет сам mem::drop
различные поля (которые также могут взывают Drop::drop
на тех областях, которые определяют его, например, b: String
здесь).
Что делает Drop::drop
do, then?
Используется для реализации дополнительных логики поверх того, что сделает компилятор; принимая ваш 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)
}
}
}
}
Здесь, в качестве исходных памятей манипулируют таким образом, непрозрачную, чтобы компилятор, эта реализация берет на себя:
- Отбрасывания каждого элемента проведен по вектору
- Освобождение памяти
Извините, я wasn ' t ясно. Я не собираюсь вручную удалять каждое поле. Мое намерение состоит в вызове функций, которые потребляют некоторые из моих полей, чтобы я мог ждать по потоку или регистрировать любые ошибки (я признаю, что я не должен был поднимать 'std :: mem :: drop', который не говорит вам любые ошибки, которые произошли). – yonran
@yonran: Ну, прося логическое обоснование, как вы это делали, является правильным вопросом, поэтому я неохотно говорю вам, что вы его редактируете и полностью меняете, вместо этого я бы посоветовал вам просто задать другой вопрос о том, как делать то, что вы хотите сделайте, желательно с MCVE на [playpen] (https://play.rust-lang.org/), с которым мы можем возиться, чтобы убедиться, что наше решение компилируется. –
@yonran: Вы когда-нибудь задавали другой вопрос? У меня такая же проблема, как у вас; в частности с использованием '' WavWriter :: finalize() '] (https://docs.rs/hound/3.0.0/hound/struct.WavWriter.html#method.finalize) в' Drop() '. – Timmmm