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

时间:2016-06-04 23:25:01

标签: reference copy rust move mutable

我想做点什么

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

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

但是编译器抱怨“无法摆脱借来的上下文”。什么是正确的方法?

2 个答案:

答案 0 :(得分:3)

反过来说:

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

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

你不能移出借来的指针,因为移动使得源无法使用,但由于你不拥有源,这些信息必须传播回所有者,Rust不支持这一点。

您可能会说"但我会在函数返回之前为*self分配一个新值!" 。问题在于,如果移动和分配之间存在恐慌,*self仍然没有有效值。如果丢弃self不是无操作,则这尤其成问题(尽管Rust不关心如果丢弃是无操作或不在此处)。

答案 1 :(得分:1)

一种选择是使用take_mut包,它提供完全符合您要求的take功能:

  

take允许从T中取出&mut T,对其执行任何操作(包括使用它),并生成另一个T以放回&mut T }。

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

  

take期间,如果发生恐慌,整个过程将会退出,因为没有有效的T可以放回&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));
    }
}