如何访问匹配表达式中绑定到变量的值的字段?

How to access fields of a value bound to a variable in a match expression?

我的代码类似于:

use std::string::{String};
use std::vec::{Vec};

enum State {
    A {
        n: usize,
        lines: Vec<String>,
    },
    B {
        n: usize,
    }
}

fn main() {
    use State::*;

    let lines = vec!["a", "b", "GO", "c", "GO", "d"];
    let mut state = B { n: 0 };
    for line in &lines {
        state = match state {
            A { n, lines } => {
                if line == &"GO" {
                    B { n: n + 1 }
                } else {
                    let mut new_lines = Vec::from(lines);
                    new_lines.push(line.to_string());
                    A { n: n, lines: new_lines }
                }
            },
            B { n } => {
                A { n: n, lines: vec![line.to_string()] }
            },
        };
    }
    let final_n = match state {
        A { n, .. } => n,
        B { n } => n,
    };
    println!("final_n = {}", final_n);
}

Rust 游乐场 link:http://is.gd/0QTYaQ

(请注意,这是对实际代码的简化。有关完整背景,请参阅此问题的 first revision。)

我想避免创建 new_lines 向量,所以我尝试将 State::A 值绑定到一个变量并访问值的字段,如下所示:

            s @ A { .. } => {
                if line == &"GO" {
                    B { n: s.n + 1 }
                } else {
                    s.lines.push(line.to_string());
                    s
                }
            },

然而,编译失败:

ParseState_enum_test.rs:23:28: 23:31 error: attempted access of field `n` on type `State`, but no field with that name was found
ParseState_enum_test.rs:23                     B { n: s.n + 1 }
                                                      ^~~
ParseState_enum_test.rs:19:5: 33:6 note: in this expansion of for loop expansion
ParseState_enum_test.rs:25:21: 25:28 error: attempted access of field `lines` on type `State`, but no field with that name was found
ParseState_enum_test.rs:25                     s.lines.push(line.to_string());
                                               ^~~~~~~
ParseState_enum_test.rs:19:5: 33:6 note: in this expansion of for loop expansion
error: aborting due to 2 previous errors

如何访问绑定到变量的值的字段?

编辑: 我知道模式绑定中的 ref mut,但我认为这对我来说不是一个好的解决方案。如果我使用 ref mut,那么我需要创建向量的克隆,因为这段代码无法编译:

            A { n, ref mut lines } => {
                if line == &"GO" {
                    B { n: n + 1 }
                } else {
                    lines.push(line.to_string());
                    A {
                        n: n,
                        lines: lines, // error: mismatched types
                    }
                }
            },

让我们看一下您的问题的更简单版本:

enum Foo {
    Alpha { score: u8 },
    Beta { lives_left: u8 },
}

fn main() {
    let the_value = Foo::Alpha { score: 42 };
    match the_value {
        alpha_only @ Alpha => println!("Score is {}", alpha_only.score),
        _ => println!("Dunno what to do!"),
    }
}

问题是枚举变体不是独立类型。也就是说,不可能有一个Foo::Alpha类型的变量;你只能让它成为 Foo 类型。您可以在错误消息中看到这一点:

attempted access of field score on type Foo, but no field with that name was found

当你使用@绑定整个模式时,你只能知道你得到的是Foo.

类型的东西

处理此问题的正常方法是使用 ref:

绑定到项目的组件
match the_value {
    Foo::Alpha { ref score } => println!("Score is {}", score),
    _ => println!("Dunno what to do!"),
}

如果您需要更改值,请使用 ref mut:

match the_value {
    Foo::Alpha { ref mut score } => {
        *score += 1;
        println!("Score is {}", score)
    },
    _ => println!("Dunno what to do!"),
}

of如果能消费价值,就不需要ref:

let the_value = Foo::Alpha { score: 42 };
let new_value = match the_value {
    Foo::Alpha { score } => Foo::Alpha { score: score + 1 },
    Foo::Beta { lives_left } => Foo::Alpha { score: lives_left * 2 },
};

以下似乎有效。它能解决问题吗?

    let new_state = match state { 
        B {n} => A { n: n, lines: vec![line.to_string()] },
        A {n, mut lines} => {
            match *line {
                "GO" => B { n: n + 1 },
                _ => {
                    lines.push(line.to_string());
                    A{ n:n, lines: lines}
                }
            }
        }
    };
    state = new_state

https://play.rust-lang.org/?gist=4fa712834999e45ccd4d&version=stable