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

Wol*_*ram 3 enums pattern-matching rust

假设我们有一个看起来像这样的枚举:

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

现在我创建了这个子类型枚举的实例,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);
        },
        _ => {}
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,以这样的方式匹配是相当繁琐的,当我们正是从上下文知道my_enum可能是唯一的 MyString.我宁愿写这样的东西(不是正确的Rust语法):

my_enum@MyString.push('!');
println!("{}", my_enum@MyString);
Run Code Online (Sandbox Code Playgroud)

如果,假设my_enum是子类型Field2,那么变异x:

my_enum@Field2.x += 1.0;
Run Code Online (Sandbox Code Playgroud)

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

patterns `Field1` and `Field2` not covered
Run Code Online (Sandbox Code Playgroud)

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

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

Fra*_*gné 7

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

然而,一些 Rust 开发人员一直在考虑让每个enum变体都是它自己的类型,这将是其自身的子类型enum

如果您的变体有许多数据字段,或者如果您附加了方法,您可以考虑将enum变体的字段包装在 a 中structstruct直接使用它,完全绕过枚举,直到出于某种原因确实需要枚举。如果您只有几个字段,并且不需要在 enum 上调用方法,那么您可能可以通过保留一个指向您在开头获得的每个字段的可变指针来摆脱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);
}
Run Code Online (Sandbox Code Playgroud)

Rust 1.26 开始,不再需要显式refref mut关键字。


Chr*_*son 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

或者,如果它只是详细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();
}
Run Code Online (Sandbox Code Playgroud)