有没有一种简单的方法来突变Rust中的枚举字段?

问题描述:

假设我们有一个看起来像这样的枚举:有没有一种简单的方法来突变Rust中的枚举字段?

enum MyEnum { 
    Field1, 
    Field2 {x: f64, y: f64}, 
    /* Maybe some other fields */ 
    MyString(String), 
} 

现在我创造了这个枚举亚型MyString的一个实例和一些动作后,我想它发生变异。例如:

fn main() { 
    let mut my_enum = MyEnum::MyString("Hello, world".to_string()); 
    /* Some actions */ 
    // Mutating the string 
    match my_enum { 
     MyEnum::MyString(ref mut content) => { 
      content.push('!'); 
     }, 
     _ => {} 
    } 
    // Printing the string 
    match my_enum { 
     MyEnum::MyString(content) => { 
      println!("{}", content); 
     }, 
     _ => {} 
    } 
} 

然而,以这样的方式匹配是相当繁琐的,当我们从上下文准确地知道my_enum可以只有MyString。我宁愿写这样的事情(不正确的锈语法):

[email protected]('!'); 
println!("{}", [email protected]); 

如果,假设,my_enum是亚型Field2的,然后发生变异x

[email protected] += 1.0; 

我能做些什么喜欢这个?我强烈地想,答案是“否”,因为如果我删除从上面的比赛_ => {},类型检查开始抱怨非详尽的模式匹配:

patterns `Field1` and `Field2` not covered 

尽管它可以推断my_enum只能是MyString。通过“推断”,我的意思是编译器可以跟踪所有类型的变量MyEnum它们可以精确包含哪些值的子类型。

我在一个更大的代码中找到了一个可以方便使用的地方,但我想我可以用其他方法重写它。不过,我认为编译器可能更聪明,并且至少要明白,在这种情况下,MyEnum::MyString模式是详尽无遗的。如果上述问题的答案真的是“否”,正如我怀疑的那样,如果Rust开发人员讨论了这个问题(也许是RFCS链接?),并且是否值得提出功能请求,我很感兴趣。

+0

'如果让MyEnum :: MyString的(参考MUT含量)= {my_enum content.push( '!'); }' – ildjarn

从Rust 1.15.1开始,编译器将无法识别特定变量在特定执行点只能是enum的特定变体。因此,你总是需要写一个详尽的match就可以了。

然而,一些开发商锈have been considering使得它使得每个enum变种将自己的类型,这将是enum本身的亚型。

如果你的变种有很多数据字段,或者如果你连有方法,你可以考虑在struct包裹enum变种的领域,并直接使用struct,完全绕过枚举,直到你需要枚举不管是什么原因。如果只有几个字段,并且不需要在枚举上调用方法,那么您可以通过将一个可变指针保留在开始时获得的每个字段中来获得详尽的match,就像这样:

fn main() { 
    let mut my_enum = MyEnum::MyString("Hello, world".to_string()); 
    let content = 
     match my_enum { 
      MyEnum::MyString(ref mut content) => content, 
      _ => unreachable!(), 
     }; 

    /* Some actions */ 
    // Mutating the string 
    content.push('!'); 

    // Printing the string 
    println!("{}", content); 
} 

如果你的代码的整个部分,其中的变量是已知的具有特定类型,你可以只把这些代码的match里面,或者如果只有一个match您关心的手臂,使用if let

fn main() { 
    let mut my_enum = MyEnum::MyString("Hello, world".to_string()); 
    /* Some actions */ 
    if let MyEnum::MyString(ref mut content) = my_enum { 
     content.push('!'); 
     //... 
     println!("{}", content); 
    } 
} 

或者,如果它只是冗长match(或if let),这是问题,你可以写的方法,使之更为简洁:

impl MyEnum { 
    fn push(&mut self, char c) { 
     if let MyEnum::MyString(ref mut content) = *self { 
      content.push(c); 
     } else { 
      unreachable!(); 
     } 
    } 

    // In practice print might be more generic, for example implement 
    // Display 
    fn print(&self) { 
     if let MyEnum::MyString(ref content) = *self { 
      println!("{}", content); 
     } 
    } 
} 

fn main() { 
    //... 
    my_enum.push('!'); 
    my_enum.print(); 
}