3

我如何说服 Rust 编译器内部match表达式在这里很好,因为外部match已经限制了可能的类型?

enum Op {
    LoadX,
    LoadY,
    Add,
}

fn test(o: Op) {
    match o {
        Op::LoadX | Op::LoadY => {
            // do something common with them for code reuse:
            print!("Loading ");

            // do something specific to each case:
            match o {
                // now I know that `o` can only be LoadX | LoadY,
                // but how to persuade the compiler?
                Op::LoadX => print!("x"), /* LoadX specific */
                Op::LoadY => print!("y"), /* LoadY specific */
                _ => panic!("shouldn't happen!"),
            }

            println!("...");
        }

        Op::Add => println!("Adding"),
    }
}

fn main() {
    test(Op::LoadX);
    test(Op::LoadY);
    test(Op::Add);
}

我尝试了两种方法,但似乎都不起作用。

  1. 命名或模式,然后使用该名称进行匹配:

    match o {
        load@(Op::LoadX | Op::LoadY) => {
        // ...
        match load {
            // ...
        }
    } 
    

    这不是有效的 Rust 语法。

  2. 命名并绑定每个构造函数:

    match o {
        load@Op::LoadX | load@Op::LoadY => {
        // ...
        match load {
           //...
        }
    } 
    

    这仍然不能满足详尽检查,因此出现相同的错误消息:

    error[E0004]: non-exhaustive patterns: `Add` not covered
      --> src/main.rs:14:19
       |
    14 |             match load {
       |                   ^ pattern `Add` not covered
    
    

有没有解决这个问题的惯用方法,或者我应该把panic!("shouldn't happen")所有地方都放在哪里或重组代码?

Rust 游乐场链接

4

2 回答 2

3

我认为你只需要重构你的代码,显然LoadX并且LoadY非常接近。所以我认为你应该创建一个重新组合它们的第二个枚举:

enum Op {
    Load(State),
    Add,
}

enum State {
    X,
    Y,
}

fn test(o: Op) {
    match o {
        Op::Load(state) => {
            // do something common with them for code reuse
            print!("Loading ");

            // do something specific to each case:
            match state {
                State::X => print!("x"),
                State::Y => print!("y"),
            }

            println!("...");
        }

        Op::Add => println!("Adding"),
    }
}

fn main() {
    test(Op::Load(State::X));
    test(Op::Load(State::Y));
    test(Op::Add);
}

这对我来说更有意义。我认为这是表达你想要的更好的方式。

于 2019-05-08T21:35:41.593 回答
2

你不能。从概念上讲,没有什么能阻止您o = Op::Add在外部匹配和内部匹配之间进行操作。变体完全有可能在两个匹配项之间发生变化。

我可能会遵循Stargateur 的代码,但如果您不想重构枚举,请记住 Rust 中有多种抽象技术。例如,函数非常适合重用代码,闭包(或特征)适合自定义逻辑。

enum Op {
    LoadX,
    LoadY,
    Add,
}

fn load<R>(f: impl FnOnce() -> R) {
    print!("Loading ");
    f();
    println!("...");
}

fn test(o: Op) {
    match o {
        Op::LoadX => load(|| print!("x")),
        Op::LoadY => load(|| print!("y")),
        Op::Add => println!("Adding"),
    }
}

fn main() {
    test(Op::LoadX);
    test(Op::LoadY);
    test(Op::Add);
}

我应该把panic!("shouldn't happen")

您应该使用unreachable!而不是,panic!因为它对程序员来说在语义上更正确。

于 2019-05-09T00:37:53.593 回答