我想更新一个枚举变量,同时将旧变量的一个字段移动到新变量,而不进行任何克隆:
enum X {
X1(String),
X2(String),
}
fn increment_x(x: &mut X) {
*x = match *x {
X::X1(s) => X::X2(s),
X::X2(s) => X::X1(s),
}
}
这不起作用,因为我们无法从&mut X
移动s
:
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:7:16
|
7 | *x = match *x {
| ^^
| |
| cannot move out of borrowed content
| help: consider removing the `*`: `x`
8 | X::X1(s) => X::X2(s),
| - data moved here
9 | X::X2(s) => X::X1(s),
| - ...and here
请不要建议实现enum X { X1, X2 }
和使用struct S { variant: X, str: String }
等。这是一个简化的示例,想象一下在变体中有许多其他字段,并希望将一个字段从一个变体移动到另一个变体。
4条答案
按热度按时间bfrts1fy1#
这不起作用,因为我们不能从
&mut X
移动s
。那就不要这样做......用值来获取结构体,然后返回一个新的:
最后,编译器会保护你,因为如果你 * 可以 * 把字符串移出枚举,那么它将处于某种半构造状态。如果函数在那一刻死机,谁来负责释放字符串?它应该释放枚举中的字符串还是局部变量中的字符串?它不能同时释放两个字符串,因为双释放是内存安全问题。
如果你必须在一个可变的引用上实现它,你可以在那里临时存储一个伪值:
创建一个空的
String
并不太糟糕(它只有几个指针,没有堆分配),但它并不总是可行的,在这种情况下,可以使用Option
:另见:
icomxhvb2#
如果你想做到这一点,而不是以零成本的方式移出值,你必须求助于一点不安全的代码(AFAIK):
u3r8eeie3#
在某些特定情况下,实际上需要的是
std::rc
jrcvhitl4#
正如@VladFrolov所评论的,an RFC proposed会给标准库
std::mem::replace_with
添加一个方法,允许你临时拥有一个可变引用后面的值,但是它没有被接受。有提供类似功能的第三方板条箱:take-mut和replace-with是我所知道的值得注意的两个函数。通过阅读文档,您可能会明白为什么标准库不接受这两个函数。如果被赋予所有权的函数出现异常,则可能会导致异常终止程序的严重后果。因为在展开继续之前,需要将 * 一些 * 值放回可变引用中。2除了恐慌之外,还有其他机制可以用来“放回”一个值。
下面是一个使用replace-with的示例: