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

Я пишу связанный список, чтобы обдумать время жизни 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 в конец списка.

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