Rust 为什么带有保护子句的匹配模式不是详尽的?

Rust 为什么带有保护子句的匹配模式不是详尽的?,rust,pattern-matching,Rust,Pattern Matching,考虑以下代码示例() 在StateMachine::transition方法中,显示的代码不编译: error[E0004]:非详尽模式:`(首字母,首字母)`未涵盖 -->src/main.rs:52:15 | 52 |匹配(开始|状态、结束|状态){ |^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^模式`(首字母,首字母)`未涵盖 但这正是我试图匹配的模式!以及(一,一)边缘和(二,二)边缘。重要的是,我特别想要这种情况,因为我想利用编译器来确

考虑以下代码示例()

StateMachine::transition
方法中,显示的代码不编译:

error[E0004]:非详尽模式:`(首字母,首字母)`未涵盖
-->src/main.rs:52:15
|
52 |匹配(开始|状态、结束|状态){
|^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^模式`(首字母,首字母)`未涵盖
但这正是我试图匹配的模式!以及
(一,一)
边缘和
(二,二)
边缘。重要的是,我特别想要这种情况,因为我想利用编译器来确保处理每一个可能的状态转换(特别是在以后添加新状态时)我知道身份转换永远是不可能的

我可以通过取消该行(
=>{}
)下面的行的注释来解决编译器错误,但这样我就失去了让编译器检查有效转换的优势,因为这将匹配将来添加的任何状态

我还可以通过手动键入每个身份转换来解决此问题,例如:

(State::Initial, State::Initial) => {}
这是一个乏味的过程,在这一点上,我只是在与编译器抗争。这可能会转化为一个宏,因此我可能会执行以下操作:

identity_is_no_op!(State);
或最坏情况:

identity_is_no_op!(State::Initial, State::One, State::Two);
宏可以在任何时候添加一个新状态时自动编写这个样板,但是当我编写的模式应该涵盖我正在寻找的确切情况时,这感觉像是不必要的工作

  • 为什么这不符合书面要求
  • 做我想做的事情最干净的方法是什么

  • 我已经决定,第二种形式的宏(即
    identity\u是\u no\u op!(State::Initial,State::One,State::Two);
    )实际上是首选解决方案

    很容易想象在未来,我确实希望某些状态在“无转换”的情况下执行某些操作。使用此宏仍然会产生预期的效果,即在添加新的
    状态时强制重新访问状态机,如果不需要执行任何操作,则只需要将新状态添加到宏arglist中。a合理的妥协

    我认为这个问题仍然有用,因为作为一个相对较新的Rustacean人,我对这种行为感到惊讶

    为什么这不符合书面要求

    因为Rust编译器在确定
    匹配
    是否详尽时无法考虑保护表达式。一旦有了保护,它就假定保护可能失败

    请注意,这与之间的区别无关。
    如果
    防护装置不是模式的一部分,则它们是
    匹配
    语法的一部分

    做我想做的事情最干净的方法是什么


    列出所有可能的组合。宏可以稍微缩短模式的写入时间,但不能使用宏替换整个
    匹配
    臂。

    或添加一个
    \u=>不可访问的!()
    如果您确信自己做得对。@rodrigo,不,我想避免使用“一网打尽”模式,以便在以后添加新状态时强制更新匹配语句。我确信我现在做得对,但试图防止将来的更改。此外,该模式并非“无法访问”(),在两个状态相等的情况下调用此函数是有效的,我只是不希望它做任何事情。@MichaelLeonard作为一般性的评论,当然最好是编译器能够静态检查不变量。如果不能,下一个最好的选择是单元测试。您需要使用
    枚举器
    strum
    派生宏>crate能够迭代枚举的所有变体,但您还需要
    \u=>unreachable!()
    如果输入了catch-all分支,就会惊慌失措。@SvenMarnach,这是一个很好的建议。在这个示例中,这显然是可行的,但实际的用例是一个生锈的嵌入式项目,不需要标准板条箱,我不确定
    货物测试
    在这种情况下是否有效(还没有尝试过)@DK.请更新您的答案,以引用可反驳与无可辩驳的模式,我会接受。虽然在这个简单的情况下,编译器理论上可以找出保护所涵盖的情况,但这在一般情况下是不可能的。保护可以包含任意表达式、函数调用和对外部状态的引用,因此在gen中一般情况下,编译器无法知道保护条件是否成立。因此,Rust总是假设它可能不成立,即使保护条件为
    if true
    ,这使得语义简单且可预测。@Stargateur,不,我自己没有说,是的,我清楚地解释了我要做的事情。我想抓住每一个在开始状态和结束状态相等之前。@Sven感谢这是有意义的,我想我希望它能穷尽这个简单的例子。@MichaelLeonard那么
    (state::Initial,state::Initial)|(state::One,state::One)|(state::Two,state::Two)=>{}的问题是什么呢
    ?这就是我要做的。解决方案已经在你问题的末尾。使用宏或直接编写。@Stargateur,它可以工作,但很乏味(我只是好奇为什么它不起作用)。我预计这个状态机将增长到包含100个状态。尽管如此,出于其他原因,我认为我建议的第二个宏实际上是更好的解决方案。
    identity_is_no_op!(State::Initial, State::One, State::Two);