Деструкторы ржавчины и собственность - PullRequest
1 голос
/ 19 октября 2019

Я чуть не задал тот же вопрос на днях, но в контексте c ++.

Я пытаюсь воспроизвести деструкторы и конструкторы в моем c-программировании. Это означает, что для каждого объекта или структуры есть функция инициализации и функция destruct, которая освобождает все ресурсы объектов следующим образом:

struct MyObject {
  struct string a;
  struct string b;
  struct string c;
};

 void ConstructMyObject(struct MyObject *obj) {
   ConstructString(&obj->a);
   ConstructString(&obj->b);
   ConstructString(&obj->c);
}

 void DestructMyObject(struct MyObject *obj) {
   DestructString(&obj->a);
   DestructString(&obj->b);
   DestructString(&obj->c);
}

Функция destruct вызывается в конце каждой области действия функции, как вРуст только, что я поместил это вручную там вместо компилятора, делающего работу для меня. Так что теперь в DestructMyObject функции я вызываю деструкторы каждого типа структурной строки, потому что для объекта структурной строки у меня также была бы функция уничтожения, написанная так же, как для объекта struct MyObject. Таким образом, все, что выделено struct MyObject, будет освобождено.

Пример с моей проблемой:

int main {
 struct MyObject Object1;
 ConstructMyObject(&Object1);
 ...
 ...
 ...
 TransferOwnershipFunction(Object1.b); /*takes a struct string object as argument*/
 ...
 ...
 ...

 DestructMyObject(&Object1);

 return 0;
}

Я передал ownernip члена (строка структуры b) Object1 другой функции,Но struct string b будет освобожден функцией main, потому что у меня есть правило, что, когда объект выходит из области видимости, я вызываю его функцию уничтожения. Но я не хочу, чтобы функция main освободила этот ресурс. TransferOwnershipFunction(...) теперь отвечает за освобождение этого члена object1. Как компилятор Rust справляется с такими ситуациями? В Rust я должен был бы сделать клон строки b?

1 Ответ

1 голос
/ 19 октября 2019

Компилятор Rust достаточно умен, чтобы видеть, когда используется только одно поле структуры. Право собственности передано только этому конкретному полю, а остальные поля удаляются в конце области (или используются иным образом). Это можно увидеть в следующем примере.

struct MyObject {
    a: String,
    b: String,
    c: String,
}

fn consume_string(_string: String) {}

fn main() {
    let object = MyObject {
        a: "".to_string(),
        b: "".to_string(),
        c: "".to_string(),
    };
    consume_string(object.b);
    // We can still access object.a and object.c
    println!("{}", object.a);
    println!("{}", object.c);
    // but not object.b
    // println!("{}", object.b);
}

(детская площадка)

Однако, если структура имеет нетривиальный деструктор, т. Е. Реализует Drop черта, тогда этого не может быть. Попытка переместить одно поле структуры приведет к ошибке компилятора, как показано ниже.

struct MyObject {
    a: String,
    b: String,
    c: String,
}

// This is new
impl Drop for MyObject {
    fn drop(&mut self) {
        println!("dropping MyObject");
    }
}

fn consume_string(_string: String) {}

fn main() {
    let object = MyObject {
        a: "".to_string(),
        b: "".to_string(),
        c: "".to_string(),
    };
    consume_string(object.b);
}

Попытка скомпилировать это приводит к ошибке

error[E0509]: cannot move out of type `MyObject`, which implements the `Drop` trait
  --> src/main.rs:22:20
   |
22 |     consume_string(object.b);
   |                    ^^^^^^^^
   |                    |
   |                    cannot move out of here
   |                    move occurs because `object.b` has type `std::string::String`, which does not implement the `Copy` trait

error: aborting due to previous error

For more information about this error, try `rustc --explain E0509`.
error: Could not compile `playground`.

(детская площадка) ( [E0509] )

Я думаю, вы уже поняли причину этого, но стоит повторить. Если Drop реализовано для структуры, деструктор может иметь нетривиальные взаимодействия между полями;они не могут быть просто отброшены независимо друг от друга. Таким образом, это означает, что структура должна оставаться единым целым до тех пор, пока она не будет отброшена.

Например, реализация Drop для Rc<T> проверяет, есть ли какие-либо (сильные) ссылки на оставленные данные, иесли нет, удаляет базовые данные. Если бы поля Rc<T> (указатель, счетчик сильных ссылок и счетчик слабых ссылок) были удалены отдельно, невозможно было бы проверить, сколько сильных ссылок осталось при удалении указателя. Не будет никакого способа сохранить исходные данные, если все еще будут сильные ссылки.

Как вы уже догадались, в случае реализации Drop вам придется клонировать поле, если вы все ещехотел потреблять это.

...