"cannot move out of borrowed content" 替换结构字段时

"cannot move out of borrowed content" when replacing a struct field

考虑这个例子:

struct Item {
    x: u32,
}

impl Item {
    pub fn increment(self, amount: u32) -> Self {
        Item { x: self.x + amount }
    }
}

struct Container {
    item: Item,
}

impl Container {
    pub fn increment_item(&mut self, amount: u32) {
        // This line causes "cannot move out of borrowed content"
        self.item = self.item.increment(amount);
    }
}

如您所见,Item.increment 消耗了该项目并且 return 是一个新实例。

Container.increment_item 中,我想用 Item.increment 编辑的 return 替换当前项目,但编译器对我大喊 cannot move out of borrowed content 错误。

Container.increment_itemselfmut所以我可以改变它的字段,我不明白为什么编译器不允许我这样做。

我知道我可以让 Container.increment_item 消耗 self 和 return 一个新对象,就像 Item.increment 那样,而且它有效,但我想了解为什么我会收到错误以及当我真的无法使用容器时如何修复它。

  • Item::increment 按值期望 self,它移动调用它的 Item
  • Container::increment_item 通过引用获取 &mut self,它允许您改变 self,但不允许您获得 self(或任何它的部分)。
  • 当您调用 self.item.increment(amount) 时,您试图按值传递 self.item,从而将所有权移至 Item::increment 函数,但您不能通过引用来执行此操作您不拥有的价值。

只需通过可变引用将 self 传递给 Item::increment,这正是可变引用的用途:

struct Item {
    x: u32,
}

impl Item {
    pub fn increment(&mut self, amount: u32) {
        self.x += amount;
    }
}

struct Container {
    item: Item,
}

impl Container {
    pub fn increment_item(&mut self, amount: u32) {
        self.item.increment(amount);
    }
}

如果您坚持要取得 Item 的所有权,那么您可以使用 mem::replace:

use std::mem;

struct Item {
    x: u32,
}

impl Item {
    pub fn increment(self, amount: u32) -> Self {
        Item { x: self.x + amount }
    }
}

struct Container {
    item: Item,
}

impl Container {
    pub fn increment_item(&mut self, amount: u32) {
        self.item = mem::replace(&mut self.item, Item { x: 0 }).increment(amount);
    }
}

但在这种情况下似乎不必要地复杂。

increment_item() 通过引用获取 Container 并且 item 无法移动(或 "consumed"),因为它位于引用后面,因为 increment() 获取 Item 按值。解决此问题的最快方法是将 Item 设为 Copy 类型。这将触发复制而不是移动(即消耗)。 playground

#[derive(Clone, Copy)]
struct Item {
    x: u32,
}

更多请参考Copy