如何更新对不可复制类型的可变引用?

How to update a mutable reference to a noncopyable type?

我想做类似的事情

impl Widget {
    fn foo(self, rhs: Widget) -> Self {
        // Some stuff
    }

    fn update(&mut self, rhs: Widget) {
        *self = (*self).foo(rhs)
    }
}

但编译器抱怨 "cannot move out of borrowed context." 执行此操作的正确方法是什么?

反过来做:

impl Widget {
    fn op(mut self, rhs: Widget) -> Self {
        self.update(rhs);
        self
    }

    fn update(&mut self, rhs: Widget) {
        // Some stuff
    }
}

你不能移出借用的指针,因为移动会使源不可用,但由于你不拥有源,该信息必须传播回所有者,而 Rust 不支持。

你可能会说 "but I'm assigning a new value to *self before the function returns!"。这样做的问题是,如果移动和赋值之间存在恐慌,*self 仍将没有有效值。如果丢弃 self 不是空操作,这尤其成问题(尽管 Rust 不关心这里是否丢弃是空操作)。

一种选择是使用 take_mut crate,它提供了 take 功能,可以完全满足您的需求:

take allows for taking T out of a &mut T, doing anything with it including consuming it, and producing another T to put back in the &mut T.

正如 所指出的,这样做的问题是如果发生恐慌,&mut 引用将处于无效状态,这可能导致未定义的行为。 take_mut 方法是:

During take, if a panic occurs, the entire process will be exited, as there's no valid T to put back into the &mut T.

这是使用 take 的代码:

extern crate take_mut;

struct Widget;

impl Widget {
    fn foo(self, rhs: Widget) -> Self {
        self
    }

    fn update(&mut self, rhs: Widget) {
        take_mut::take(self, |s| s.foo(rhs));
    }
}