我想更新枚举变体,同时将旧变体的字段移动到新变体而不进行任何克隆:
String id = "HERE+IS+ID";
ODocument location = new ODocument("OPoint");
location.field("coordinates", Arrays.asList(12.2323, 34.3233));
Vertex sink = graph.addVertex(id, Constants.NAME, "Sink-Root", Constants.LOCATION, location);
这不起作用,因为我们无法从enum X {
X1(String),
X2(String),
}
fn increment_x(x: &mut X) {
match x {
&mut X::X1(s) => {
*x = X::X2(s);
}
&mut X::X2(s) => {
*x = X::X1(s);
}
}
}
移动s
。
请不要建议实施&mut X
和使用enum X { X1, X2 }
等内容。这是一个简化的示例,假设在变体中有很多其他字段,并且想要移动一个字段一种变体到另一种变体。
答案 0 :(得分:11)
这不起作用,因为我们无法从
s
移动&mut X
。
然后不要这样做......按值取结构并返回一个新结构:
enum X {
X1(String),
X2(String),
}
fn increment_x(x: X) -> X {
match x {
X::X1(s) => X::X2(s),
X::X2(s) => X::X1(s),
}
}
最终,编译器正在保护您,因为如果您可以将字符串移出枚举,那么它将处于某种半构造状态。如果函数在那个确切的时刻出现恐慌,谁将负责释放字符串?它应该释放枚举中的字符串还是局部变量中的字符串。它既不是双重自由也是一个记忆安全问题。
如果你必须在可变参考上实现它,你可以暂时存储一个虚拟值:
use std::mem;
fn increment_x_inline(x: &mut X) {
let old = mem::replace(x, X::X1(String::new()));
*x = increment_x(old);
}
创建一个空的String
并不是太糟糕(它只是几个指针,没有堆分配),但它并不总是可行的。在这种情况下,您可以使用Option
:
fn increment_x_inline(x: &mut Option<X>) {
let old = x.take();
*x = old.map(increment_x);
}
答案 1 :(得分:4)
如果你想以零成本的方式移出价值,你就不得不诉诸一些不安全的代码(AFAIK):
#[derive(Debug)]
enum X {
X1(String),
X2(String),
}
fn increment_x(x: &mut X) {
let interim = unsafe { mem::uninitialized() };
let prev = mem::replace(x, interim);
let next = match prev {
X::X1(s) => X::X2(s),
X::X2(s) => X::X1(s),
};
let interim = mem::replace(x, next);
mem::forget(interim); // Important! interim was never initialized
}
编辑:感谢@Shepmaster考虑了替换...