【问题标题】:"cannot move out of borrowed content" when replacing a struct field [duplicate]替换结构字段时“无法移出借用的内容”[重复]
【发布时间】:2019-06-13 12:38:19
【问题描述】:

考虑这个例子:

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 使用该项目并返回一个新实例。

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

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

我知道我可以让Container.increment_item 消耗self 并返回一个新对象,就像Item.increment 一样,它可以工作,但我想了解为什么我会收到错误以及如何修复当我真的不能消耗容器时。

【问题讨论】:

    标签: rust borrow-checker


    【解决方案1】:
    • 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);
        }
    }
    

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

    【讨论】:

    • (或其任何部分) 谢谢,这就是我错过的关于结构所有权的问题,现在我明白了这个错误。不幸的是,在我的真实世界案例中,Item(因此Item::increment)来自第三方API,所以我不能只在那里实现mut,而且构造Item 的新实例也不容易,所以mem::replace 解决方案不是我所期望的。我将针对这个具体案例提出一个新问题。
    【解决方案2】:

    increment_item() 通过引用获取 Container,而 item 在引用后面时无法移动(或“使用”),因为 increment() 通过值获取 Item。解决此问题的最快方法是将Item 设为Copy 类型。这将触发复制而不是移动(即消耗)。 playground

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

    更多内容请参考Copy

    【讨论】:

    • 请解释否决票!
    • 不是反对者,但你不能复制每个项目。因此,它并不是适用于所有用例的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-04
    • 2019-08-29
    • 2019-08-23
    • 1970-01-01
    • 2015-04-20
    相关资源
    最近更新 更多