gpt4 book ai didi

rust - 为什么带有保护子句的匹配模式不是详尽无遗的?

转载 作者:行者123 更新时间:2023-11-29 07:54:49 25 4
gpt4 key购买 nike

考虑以下代码示例 ( playground )。

#[derive(PartialEq, Clone, Debug)]
enum State {
Initial,
One,
Two,
}

enum Event {
ButtonOne,
ButtonTwo,
}

struct StateMachine {
state: State,
}

impl StateMachine {
fn new() -> StateMachine {
StateMachine {
state: State::Initial,
}
}

fn advance_for_event(&mut self, event: Event) {
// grab a local copy of the current state
let start_state = self.state.clone();

// determine the next state required
let end_state = match (start_state, event) {
// starting with initial
(State::Initial, Event::ButtonOne) => State::One,
(State::Initial, Event::ButtonTwo) => State::Two,

// starting with one
(State::One, Event::ButtonOne) => State::Initial,
(State::One, Event::ButtonTwo) => State::Two,

// starting with two
(State::Two, Event::ButtonOne) => State::One,
(State::Two, Event::ButtonTwo) => State::Initial,
};

self.transition(end_state);
}

fn transition(&mut self, end_state: State) {
// update the state machine
let start_state = self.state.clone();
self.state = end_state.clone();

// handle actions on entry (or exit) of states
match (start_state, end_state) {
// transitions out of initial state
(State::Initial, State::One) => {}
(State::Initial, State::Two) => {}

// transitions out of one state
(State::One, State::Initial) => {}
(State::One, State::Two) => {}

// transitions out of two state
(State::Two, State::Initial) => {}
(State::Two, State::One) => {}

// identity states (no transition)
(ref x, ref y) if x == y => {}

// ^^^ above branch doesn't match, so this is required
// _ => {},
}
}
}

fn main() {
let mut sm = StateMachine::new();

sm.advance_for_event(Event::ButtonOne);
assert_eq!(sm.state, State::One);

sm.advance_for_event(Event::ButtonOne);
assert_eq!(sm.state, State::Initial);

sm.advance_for_event(Event::ButtonTwo);
assert_eq!(sm.state, State::Two);

sm.advance_for_event(Event::ButtonTwo);
assert_eq!(sm.state, State::Initial);
}

StateMachine::transition方法,所提供的代码无法编译:

error[E0004]: non-exhaustive patterns: `(Initial, Initial)` not covered
--> src/main.rs:52:15
|
52 | match (start_state, end_state) {
| ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `(Initial, Initial)` not covered

但这正是我要匹配的模式!随着 (One, One)边缘和 (Two, Two)边缘。重要的是,我特别想要这种情况,因为我想利用编译器来确保处理每个可能的状态转换(尤其是在稍后添加新状态时),并且我知道身份转换将始终是空操作。

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

我也可以通过手动输入每个身份转换来解决这个问题,例如:

(State::Initial, State::Initial) => {}

这很乏味,那时我只是在与编译器作斗争。这可能会变成一个宏,所以我可能会做类似的事情:

identity_is_no_op!(State);

或者最坏的情况:

identity_is_no_op!(State::Initial, State::One, State::Two);

每当添加新状态时,宏都可以自动编写此样板文件,但当我编写的模式应该涵盖我正在寻找的确切情况时,这感觉像是不必要的工作。

  1. 为什么这不像写的那样有效?
  2. 做我想做的事情最干净的方法是什么?

我认为第二种形式的宏(即 identity_is_no_op!(State::Initial, State::One, State::Two); )实际上是首选解决方案。

很容易想象 future 我确实希望某些州在“无过渡”情况下做某事。使用这个宏仍然会产生预期的效果,即在新的 State 时强制重新访问状态机。 s 被添加,如果不需要做任何事情,只需要将新状态添加到宏 arglist 中。 IMO 的合理妥协。

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

最佳答案

Why doesn't this work as written?

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

请注意,这与refutable and irrefutable patterns 之间的区别无关。 . if 守卫不是模式的一部分,它们是match 语法的一部分。

What is the cleanest way to do what I am trying to do?

列出所有可能的组合。宏可以稍微缩短编写模式的时间,但您不能使用宏来替换整个match arm。

关于rust - 为什么带有保护子句的匹配模式不是详尽无遗的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53166245/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com