rust match in match: "spurious" 简单枚举的非详尽错​​误:如何 "propagate" 枚举变量类型知识?

rust match in match: "spurious" non exhaustive error on simple enums: how to "propagate" the enum variable-kind knowledge?

我遇到了一个有趣的虚假“比赛中的比赛”非详尽生锈错误。做个简单的小例子(当然生产案例更有意思):

pub enum SomeEnum {
    Possibility1,
    Possibility2,
    Possibility3
}

pub fn do_something(some: &SomeEnum){
    match some {
        SomeEnum::Possibility1 => println!("this is possibility 1"),
        something_else => {
            println!("this is not possibility 1");

            match something_else {
                SomeEnum::Possibility2 => println!("but this is 2"),
                SomeEnum::Possibility3 => println!("but this is 3")
            }
        }
    }
}

编译不通过,报错:

error[E0004]: non-exhaustive patterns: `&Possibility1` not covered
  --> src/main.rs:13:19
   |
1  | / pub enum SomeEnum {
2  | |     Possibility1,
   | |     ------------ not covered
3  | |     Possibility2,
4  | |     Possibility3
5  | | }
   | |_- `SomeEnum` defined here
...
13 |               match something_else {
   |                     ^^^^^^^^^^^^^^ pattern `&Possibility1` not covered
   |
   = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms
   = note: the matched value is of type `&SomeEnum`

我理解这个错误,我知道如何“粗暴地”修复它(类似于 添加一个:

_ => unreachable!();

例如分支)。

不过,我觉得这很可悲:编译器足够聪明,在第一次匹配时就知道 something_elsePossibility2Possibility3 类型,而折腾把这些知识写下来?

因此我的问题是:是否有一种简单、惯用的方法可以强制 Rust 将这种知识从外部匹配“传播”到内部匹配,这样我就可以在不粗暴地匹配所有输入的情况下抑制错误?同样,在这个简单的例子中,_ 匹配看起来很简单,但在一些更复杂的代码中,我希望编译器帮助我检查我的逻辑是否正确,这不符合使用 _ 匹配所有过滤器。

Still, I feel this is very sad: the compiler is smart enough at the first match to know that the something_else is either of kind Possibility2 or Possibility3

不是,真的。从语义上讲,它只是一个接一个地尝试每个匹配臂,然后取第一个。

My question is therefore: is there a simple, idiomatic way to force rust to "propagate" this kind knowledge from the outer match to the inner match

没有。 rustc 在类型级别(主要)的原因,就其而言 something_else 是一个 SomeEnum,而 SomeEnum 有一个 SomeEnum::Possibility1。如果你想要一个子枚举,你必须提供一个。

如果 RFC 2593 Enum Variant Types(或像 OCaml 风格的多态变体这样的替代方案)再次被提交 并被接受 并实施。但除此之外,您必须在您的枚举上“手动”实现它。

我所说的“手工完成”是指将您想要的语义手动编码为附加枚举,例如

pub enum SomeEnum {
    Possibility1,
    Possibility2,
    Possibility3
}

enum IsOne { One, Other(Sub23) }
enum Sub23 {
    Sub2,
    Sub3
}
impl SomeEnum {
    fn is_one(&self) -> IsOne {
        match self {
            Self::Possibility1 => IsOne::One,
            Self::Possibility2 => IsOne::Other(Sub23::Sub2),
            Self::Possibility3 => IsOne::Other(Sub23::Sub3),
        }
    }
}

pub fn do_something(some: &SomeEnum){
    match some.is_one() {
        IsOne::One => println!("this is possibility 1"),
        IsOne::Other(sub) => {
            println!("this is not possibility 1");

            match sub {
                Sub23::Sub2 => println!("but this is 2"),
                Sub23::Sub3 => println!("but this is 3")
            }
        }
    }
}