看下面的代码,感受一下神奇的写法。
match分支返回的是bool,这是明确的,但前两条分支并没有返回什么,而是提前结束了函数。
在常见的match写法中,每个分支的返回值都应该保持一致的类型。但这里打破了这个规则。
fn tick_node(
n: &mut NodeMut<BehaveNode>,
time: &Res<Time>,
commands: &mut Commands,
tick_ctx: &TickCtx,
) -> BehaveNodeStatus {
use BehaveNode::*;
// if logging {
// info!("tick_node: {:?} = {}", n.id(), n.value());
// }
// short circuit nodes that have already got a result
let reset_needed = match n.value().status() {
Some(BehaveNodeStatus::Success) => return BehaveNodeStatus::Success,
Some(BehaveNodeStatus::Failure) => return BehaveNodeStatus::Failure,
Some(BehaveNodeStatus::PendingReset) => true,
_ => false,
};
if reset_needed {
*n.value().status_mut() = Some(BehaveNodeStatus::Running);
reset_descendants(n);
}
if else
在if else中进行return是很常见的。match分支也支持return.
- match底层是不是基于if else的 ? 不是! match有模式匹配,比if else更加复杂高效。
不是if else的语法糖. - match和if else的相同点: 都会汇编成跳转指令。
- 不同点:if else 可能有多个分支,一个分支没匹配就匹配下一个,找到匹配后执行,并跳过后续分支; match只有1跳,基于一个跳表实现的。
match的跳表不是无限的,所以match有穷尽性检查。
match中的模式匹配
match tick_node(&mut only_child, time, commands, tick_ctx) {
// if our child node completes, reset next tick so we can run it again
BehaveNodeStatus::Success | BehaveNodeStatus::Failure => {
*n.value().status_mut() = Some(BehaveNodeStatus::PendingReset);
BehaveNodeStatus::PendingReset
}
BehaveNodeStatus::PendingReset => BehaveNodeStatus::Running,
other => other,
}
other是匹配并绑定变量,和下面的类似的写法没有任何差异。
match x {
v @ 1 => println!("匹配到了 1,绑定的变量是 {}", v),
2 => println!("匹配到了 2"),
other => println!("匹配到了其他值: {}", other),
}
v @ 1:这个写法是 Rust 的绑定模式,意思是:“如果 x 是 1,那就把它绑定到变量 v 上,同时进入这个分支。”
other是匹配了剩余的情况,名字无所谓 a b c均可。如果分支中没有用到此变量,按推荐需要使用_。
下面的例子是不是很熟悉。
match x {
1 => println!("匹配到了 1,绑定的变量是 {}", v),
2 => println!("匹配到了 2"),
_ => println!("匹配到了其他值", ),
}
@的更多用法
下面例子和上面例子中的@用法是一样的,都是绑定变量,给分支逻辑使用。
只不过下面是对struct进行模式匹配。
match x {
TriggerReq {
task_status: task_status @ TriggerTaskStatus::NotTriggered,
status,
trigger,
} => {
let ctx = BehaveCtx::new_for_trigger(task_node, tick_ctx);
commands.dyn_trigger(trigger.clone(), ctx);
// Don't use AwaitingTrigger for this, because of ordering issues..
// the trigger response arrives BEFORE we insert the BehaveAwaitingTrigger component,
// so the trigger response handler can't remove it, so it never ticks.
*task_status = TriggerTaskStatus::Triggered;
*status = Some(BehaveNodeStatus::Running);
BehaveNodeStatus::Running
}
#[rustfmt::skip]
TriggerReq {task_status: TriggerTaskStatus::Complete(true), status, ..} => {
*status = Some(BehaveNodeStatus::Success);
BehaveNodeStatus::Success
}
#[rustfmt::skip]
TriggerReq {task_status: TriggerTaskStatus::Complete(false), status, ..} => {
*status = Some(BehaveNodeStatus::Failure);
BehaveNodeStatus::Failure
}
// in this case, the trigger didn't report a result immediately, so we go into awaiting trigger mode.
// this happens if the trigger hands the ctx off to another system to report the result later.
#[rustfmt::skip]
TriggerReq {task_status: TriggerTaskStatus::Triggered, status, .. } => {
*status = Some(BehaveNodeStatus::AwaitingTrigger);
BehaveNodeStatus::AwaitingTrigger
}
}
另一个有趣的是:模式匹配针对同一个struct,字段值不一样时可以独立成分支,非常有意思,让逻辑表达的更加简洁。