mrjbom
@mrjbom

Вызов drop в вызове drop?

При освобождении структуры я хочу сначала освободить одно из её полей, однако я не могу сделать это.
fn main() {
    let a = A {
        b: B,
    };
    drop(a);
}

struct A {
    b: B
}

impl Drop for A {
    fn drop(&mut self) {
        drop(self.b); // cannot move out of `self.b` which is behind a mutable reference
        println!("Drop A")
    }
}

struct B;

impl Drop for B {
    fn drop(&mut self) {
        println!("Drop B")
    }
}


Тут встаёт два вопроса:
Почему drop принимает ссылку, а не значение? Причём если вызывать drop() руками, то он принимает значение, а в объявлении при реализации типажа - мутабельная ссылка.
Почему сначала вызывается drop для A, а потом для B? По логике drop должен сначала вызываться для полей.

И как мне в итоге сделать то, что я хочу? Т.е. в drop для A вызвать drop B.
Rust Book демонстрирует какой-то костыль с Option<>, который будет только мешаться в остальной части кода.
  • Вопрос задан
  • 71 просмотр
Решения вопроса 1
bingo347
@bingo347
Crazy on performance...
Почему drop принимает ссылку, а не значение?

Потому что метод drop трейта Drop вызывается компилятором, каждый раз когда переменная владеющая чем-либо выходит из области видимости. И сам метод drop тут не исключение. То есть если бы self тут был по значению, компилятор был бы обязан его дропнуть в конце функции, что вызвало бы бесконечную рекурсию.

Причём если вызывать drop() руками
Функция core::mem::drop никакого отношения к трейту Drop не имеет. Если Вы глянете на её реализацию, то это просто пустая функция, которая принимает аргумент по значению, а он уже дропается на общих основаниях, так как выходит из области видимости в ней.

Почему сначала вызывается drop для A, а потом для B? По логике drop должен сначала вызываться для полей.
У Вас неверная логика. В метод трейта Drop приходит ссылка, а значит должна быть гарантия того что данные по ней полностью валидные. Всегда дропается сначала внешняя структура,а затем её поля. Более того компилятор не даст Вам даже мувнуть части структуры имплиментирующей Drop.

Если очень нужно, то владение из поля можно забрать через std::mem::swap/std::mem::replace/std::mem::take
Хотя проще это сделать обернув такое поле в Option и забирая владение его методом take
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы