尝试转移所有权时,无法移动借用内容

27

我正在编写一个链表来理解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 时,可能会发生所有权的变化。

如果可能的话,我不想克隆该值,因为那样会浪费空间。我也不想仅在函数执行期间“借用”它。我真的想转移其所有权。

我该怎么做?

我已经查看了 cannot move out of borrowed content when unwrapping a member variable in a &mut self methodCannot move out of borrowed content / cannot move out of behind a shared reference

我尝试按建议中的方法删除匹配分支,并在新的 LinkedListNode 创建时定义 next,但我仍然收到相同的错误消息。

我已成功添加了一个 append 方法,该方法接受一个要添加到列表末尾的 LinkedListNode


1个回答

52

尝试转移所有权时无法移出已借用的内容

从高层次来看,这违背了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
} 

你认为这两个解决方案中哪一个更符合惯用代码的风格? - Gilles
1
由于您有一个 Node 和一个包装器 List,我可能会使用带有 take 的版本;我认为人们更容易理解 &mut self 而不是 self。如果列表只由 Node 组成,则必须使用 self 版本。我可能还会注释为 #[must_use],以帮助指导用户。 - Shepmaster
@Shepmaster 非常感谢,我完全不知道 take 可以这样使用。 - Gabu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接