Match的分支不需要全部返回相同的类型

看下面的代码,感受一下神奇的写法。

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中进行return是很常见的。match分支也支持return.

  1. match底层是不是基于if else的 ? 不是! match有模式匹配,比if else更加复杂高效。不是if else的语法糖.
  2. match和if else的相同点: 都会汇编成跳转指令。
  3. 不同点:if else 可能有多个分支,一个分支没匹配就匹配下一个,找到匹配后执行,并跳过后续分支; match只有1跳,基于一个跳表实现的。

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,字段值不一样时可以独立成分支,非常有意思,让逻辑表达的更加简洁。