Невозможно выйти из заимствованного контента при попытке передать право собственности

Я пишу связанный список, чтобы разобраться в сроках жизни, владении и ссылках на Rust. У меня такой код:

pub struct LinkedList {
    head: Option<Box<LinkedListNode>>,
}

pub struct LinkedListNode {
    next: Option<Box<LinkedListNode>>,
}

impl LinkedList {
    pub fn new() -> LinkedList {
        LinkedList { head: None }
    }

    pub fn prepend_value(&mut self) {
        let mut new_node = LinkedListNode { next: None };

        match self.head {
            Some(ref head) => new_node.next = Some(*head),
            None => new_node.next = None,
        };

        self.head = Some(Box::new(new_node));
    }
}

fn main() {}

Но я получаю следующую ошибку компиляции:

error[E0507]: cannot move out of borrowed content
  --> src/main.rs:18:52
   |
18 |             Some(ref head) => new_node.next = Some(*head),
   |                                                    ^^^^^ cannot move out of borrowed content

В более новых версиях Rust немного другая ошибка:

error[E0507]: cannot move out of `*head` which is behind a shared reference
  --> src/main.rs:18:52
   |
18 |             Some(ref head) => new_node.next = Some(*head),
   |                                                    ^^^^^ move occurs because `*head` has type `std::boxed::Box<LinkedListNode>`, which does not implement the `Copy` trait

Я думаю, что узел head в настоящее время должен принадлежать self, который является связанным списком. Когда я назначу его new_node.next, вероятно, произойдет смена владельца.

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

Как я могу это сделать?

Я уже видел, что не может выйти из заимствованного содержимого, когда разворачивание переменной-члена в методе & mut self и Невозможно выйти из заимствованного содержимого / не может выйти из-за общего отпечатка.

Я попытался удалить спичку, как это было предложено в принятом ответе на один из этих вопросов, и определил next при создании нового LinkedListNode, но получаю то же сообщение об ошибке.

Я успешно добавил append метод, который требует LinkedListNode, чтобы добавить его в конец списка.


person Gilles    schedule 01.02.2015    source источник


Ответы (1)


Невозможно выйти из заимствованного контента при попытке передать право собственности

На высоком уровне это противоречит здравому смыслу Rust. Вы не можете передать право собственности на что-то заимствованное, потому что вы не владеете этим. Вы не должны брать мою машину (&Car), а затем отдавать ее первому, кого встретите на улице! Это по-прежнему верно, даже если я одолжу вам свою машину и позволю вам внести в нее изменения (&mut Car).

Вы вообще не можете переместить head из &self, потому что вы не можете изменить значение.

Вы не можете переместить head из &mut self, потому что это оставит структуру LinkedList в несогласованном состоянии - одно из полей будет иметь неопределенное значение. Это основная мера гарантий безопасности Rust.

В общем, вам нужно будет следовать чему-то из Как я могу заменить новое значение поля в изменяемой ссылке на структуру? для замены существующего значения.

В этом случае вы можете использовать Option::take. Это оставит переменную на месте, заменив ее на None и вернув предыдущее значение. Затем вы можете использовать это значение для создания нового заголовка списка:

pub fn prepend_value(&mut self) {
    let head = self.head.take();
    self.head = Some(Box::new(LinkedListNode { next: head }));
}

Более общее решение - стать владельцем структуры вместо того, чтобы заимствовать ее. Это позволяет вам делать с ним все, что вы хотите. Обратите внимание, что мы берем self по значению, а не по ссылке:

pub fn prepend_value(mut self) -> LinkedList {
    self.head = Some(Box::new(LinkedListNode { next: self.head }));
    self
} 
person Shepmaster    schedule 01.02.2015
comment
Какое из двух решений вы бы назвали более идиоматическим кодом? - person Gilles; 02.02.2015
comment
Поскольку у вас есть Node и оболочка List, я бы, вероятно, использовал версию с take; Я думаю, люди понимают &mut self больше, чем self. Если бы список состоял только из Node, вам пришлось бы использовать версию self. Я бы, наверное, тоже обозначил его как #[must_use], чтобы помочь пользователям. - person Shepmaster; 02.02.2015