Enums 是否可以将可变引用值中的变量切换到枚举?

Enums 是否可以将可变引用值中的变量切换到枚举?,enums,rust,Enums,Rust,是否可以切换可变参考值(&mut E)中的变量,而无需对T附加限制,也无需使用不安全的代码 即,给定一个枚举: enum E<T> { VariantA(T), VariantB(T) } enum E{ 瓦里安塔(T), 瓦里安TB(T) } 正确的书写方式是什么: let x: E<???> = E::VariantA(??); change_to_variant_b(&mut x); assert_eq!(x, E::VariantB(?

是否可以切换可变参考值(
&mut E
)中的变量,而无需对
T
附加限制,也无需使用不安全的代码

即,给定一个枚举:

enum E<T> {
    VariantA(T),
    VariantB(T)
}
enum E{
瓦里安塔(T),
瓦里安TB(T)
}
正确的书写方式是什么:

let x: E<???> = E::VariantA(??);
change_to_variant_b(&mut x);
assert_eq!(x, E::VariantB(??));
设x:E=E::VariantA(??);
将_更改为_变体_b(&mut x);
断言!(x,E::VariantB(??);

我要在这里冒险说


只需对签名稍作更改即可:

fn change_to_variant_b<T>(e: E<T>) -> E<T> {
    match e {
        E::VariantA(t) => E::VariantB(t),
        E::VariantB(t) => E::VariantB(t),
    }
}

可以使用附加边界(
默认值
,或
克隆
):

fn将变量更改为变量(e:&mut e){
匹配std::mem::replace(e,e::VariantA(T::default()){
E::VariantA(t)=>E=E::VariantB(t),
E::VariantB(t)=>E=E::VariantB(t),
}
}
是否可以在可变引用的值中切换变量

因此,一般的答案是“不”。原因是这样做会使枚举处于不确定状态,这将允许访问未定义的内存,这将允许打破Rust的安全保证。作为示例,让我们假设此代码编译时没有错误:

impl<T> E<T> {
    fn promote_to_b(&mut self)  {
        if let E::VariantA(val) = *self {
            // Things happen
            *self = E::VariantB(val);
        }
    }
}
impl{
fn将_升级到_b(&mut self){
如果让E::VariantA(val)=*self{
//事情发生了
*self=E::VariantB(val);
}
}
}
问题是,一旦将值从
self
移动到
val
,表示
T
的内存在
self
中会发生什么情况

如果我们复制了位,然后在“事情发生”中发生了恐慌,则
val
T
内部
self
的析构函数将运行,但由于这些点位于相同的数据,这将导致双自由

如果我们不复制这些位,那么您就无法安全地访问“Things Aching”(事情发生)中的
val
,这将是一个微不足道的有用信息


“按值”解决方案之所以有效,是因为编译器可以跟踪应该调用析构函数的人:函数本身。一旦进入函数内部,编译器就会知道哪些特定行可能需要释放该值,并在出现死机时正确调用它们

克隆
默认
解决方案有效,因为您从未将值移出原始枚举。相反,您可以使用伪值替换原始枚举,并获得原始枚举的所有权(使用
默认值
)或复制整个原始值(使用
克隆



建议添加一种方法,以确保正确的内存语义被支持,但RFC没有被接受。

< P>作为这种特殊情况的替代,可以考虑用<代码> T < /C>和一个简单的枚举:
struct Outer<T> {
    val: T,
    kind: Inner,
}

impl<T> Outer<T> {
    fn promote_to_b(&mut self) {
        self.kind.promote_to_b()
    }
}

enum Inner {
    VariantA,
    VariantB,
}

impl Inner {
    fn promote_to_b(&mut self) {
        if let Inner::VariantA = *self {
            *self = Inner::VariantB;
        }
    }
}
struct-Outer{
瓦尔:T,
种类:内部,
}
内部外部{
fn将_升级到_b(&mut self){
self.kind.promotion_to_b()
}
}
枚举内部{
瓦里纳塔,
瓦里安,
}
impl内部{
fn将_升级到_b(&mut self){
如果让内部::VariantA=*self{
*self=内部::VariantB;
}
}
}

看起来像是封闭式结构可以解决的问题。或者,将其更改为带有
T
和普通
enum
struct
对您的情况有效吗?@ChrisEmerson非常有效。我只是认为变体必须有一种方法来假定其兄弟的内容,因为它直观上并不与Rust的所有权模型背道而驰。我不确定这是否与原始问题足够接近,以保证有一个答案,但如果您愿意拥有一个不包含
t
的变体,并容忍“不可能”的路径通过代码,我已经编写了一个安全、稳定的
change\u to\u variant\u b
版本,它使用
std::mem::replace
。道具以获得清晰的答案并详细列出备选方案。我需要
&mut E
,因为我在一个切片上进行可变迭代,所以我不能(?)暂时将元素移出,以使第一个解决方案生效。另外,作为一个典范解决方案,请参见@Doe:Oh,如果Chris解决方案对您有效,那么一定要使用它。在简化的示例中,总是有点难想出替代方案,因为不清楚哪些是可以更改的,哪些是不能更改的。@maybe”“解决方案”不是描述Chris评论的最佳词汇(因为它完全回避了问题)。然而,对于大多数正在寻找解决方案的访问者来说,Chris的评论可能就足够了。不可能是唯一符合OP标准的答案。@Doe:是的,Chris和我一样,都是更多的变通方法。如果你是(1)坚持使用
enum
,(2)坚持使用函数签名,以及(3)坚持使用无不安全/无边界…那么您就不能这样做。
impl<T> E<T> {
    fn promote_to_b(&mut self)  {
        if let E::VariantA(val) = *self {
            // Things happen
            *self = E::VariantB(val);
        }
    }
}
struct Outer<T> {
    val: T,
    kind: Inner,
}

impl<T> Outer<T> {
    fn promote_to_b(&mut self) {
        self.kind.promote_to_b()
    }
}

enum Inner {
    VariantA,
    VariantB,
}

impl Inner {
    fn promote_to_b(&mut self) {
        if let Inner::VariantA = *self {
            *self = Inner::VariantB;
        }
    }
}