是否有一种简单的方法来改变Rust中的枚举字段?

时间:2017-02-25 23:36:49

标签: enums rust pattern-matching

假设我们有一个如下所示的枚举:

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时,以这种方式进行匹配非常麻烦。我宁愿写这样的东西(不是正确的Rust语法):

my_enum@MyString.push('!');
println!("{}", my_enum@MyString);

如果假设my_enum属于Field2子类型,那么改为x

my_enum@Field2.x += 1.0;

我可以这样做吗?我强烈认为答案是"否",因为如果我从上面的匹配中删除_ => {},则输入checker开始抱怨非详尽的模式匹配:

patterns `Field1` and `Field2` not covered

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

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

2 个答案:

答案 0 :(得分:6)

如果您有一整段代码,其中已知变量具有特定类型,您可以将该代码放在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();
}

答案 1 :(得分:4)

从Rust 1.15.1开始,编译器无法识别特定变量在某些执行点只能是enum的特定变体。因此,您总是需要在其上写一个详尽的match

然而,一些Rust开发人员have been considering使得每个enum变体都是它自己的类型,这将是enum本身的子类型。

如果您的变体包含许多数据字段,或者您附加了方法,则可以考虑将enum变体的字段包含在struct中并使用struct直接,完全绕过枚举,直到你因任何原因需要枚举。如果你只有几个字段,并且不需要在枚举上调用方法,那么你可能只需保留一个可变指针,指向你在开头获得的每个字段,并且详尽无遗{{ 1}},像这样:

match