【问题标题】:Cannot move out of borrowed content when trying to transfer ownership尝试转移所有权时无法移出借用的内容
【发布时间】:2015-03-31 06:23:23
【问题描述】:

我正在编写一个链表来了解 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 添加到列表末尾。

【问题讨论】:

标签: reference rust move-semantics borrow-checker


【解决方案1】:

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

在高层次上,这对 Rust 来说是不合规矩的。你不能转让借来的东西的所有权因为你不拥有它。你不应该借我的车(&amp;Car)然后把它交给你在街上看到的第一个人!即使我将汽车借给您并允许您对其进行更改,这仍然是正确的 (&amp;mut Car)。

您根本无法将head 移出&amp;self,因为您无法改变值。

您不能将head 移出&amp;mut self,因为这会使LinkedList 结构处于不一致的状态——其中一个字段将具有未定义的值。这是 Rust 安全保证的核心衡量标准。

通常,您需要遵循 How can I swap in a new value for a field in a mutable reference to a structure? 中的某些内容来替换现有值。

在这种情况下,您可以使用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
} 

【讨论】:

  • 您会说两种解决方案中哪一种更符合地道代码?
  • 既然你有一个Node 和一个包装器List,我可能会使用带有take 的版本;我认为人们比self 更了解&amp;mut self。如果列表仅由Nodes 组成,则必须使用self 版本。我可能也会将其注释为#[must_use],以帮助指导用户。
  • @Shepmaster 非常感谢,我不知道 take 被这样使用了。
猜你喜欢
  • 2018-04-04
  • 1970-01-01
  • 2015-04-20
  • 1970-01-01
  • 2015-03-25
  • 1970-01-01
  • 2017-10-15
  • 2015-08-02
  • 2018-09-19
相关资源
最近更新 更多