我如何放松非排量模式检查已知变体的嵌套匹配



我如何说服生锈编译器内部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. 命名为or-pattern,然后使用该名称匹配:

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

    那不是有效的生锈语法。

  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 Playground链接

你不能。从概念上讲,没有什么可以阻止您在外部匹配和内部匹配之间进行o = Op::Add。变体完全有可能在两场比赛之间进行更改。

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

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!,因为它对程序员更正确。

我认为您只需要重构代码,显然LoadXLoadY非常接近。因此,我认为您应该创建第二个枚举来重组它们:

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);
}

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

相关内容

  • 没有找到相关文章

最新更新