我想在不使用任何克隆的情况下将旧变体的字段移动到新变体时更新枚举变体:
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),
}
}
Run Code Online (Sandbox Code Playgroud)
这并不工作,因为我们不能移动s的&mut X.
请不要建议实现enum X { X1, X2 }和使用struct S { variant: X, str: String }等等.这是一个简化的例子,想象在变体中有许多其他字段,并希望将一个字段从一个变量移动到另一个变体.
She*_*ter 12
这并不工作,因为我们不能移动
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),
}
}
Run Code Online (Sandbox Code Playgroud)
最终,编译器正在保护您,因为如果您可以将字符串移出枚举,那么它将处于某种半构造状态.如果功能在那个确切的时刻恐慌,谁将负责释放字符串?它应该释放枚举中的字符串还是局部变量中的字符串.它不能同时作为双重免费是一个内存安全问题.
如果你必须在可变引用上实现它,你可以暂时存储一个虚拟值:
use std::mem;
fn increment_x_inline(x: &mut X) {
let old = mem::replace(x, X::X1(String::new()));
*x = increment_x(old);
}
Run Code Online (Sandbox Code Playgroud)
创建一个空String的并不是太糟糕(它只是几个指针,没有堆分配),但它并不总是可能的.在这种情况下,您可以使用Option:
fn increment_x_inline(x: &mut Option<X>) {
let old = x.take();
*x = old.map(increment_x);
}
Run Code Online (Sandbox Code Playgroud)
如果你想在不以零成本的方式移出价值的情况下做到这一点,你必须求助于一些不安全的代码(AFAIK):
use std::mem;
#[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
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1686 次 |
| 最近记录: |