通配符 _ 与 .. 的优先级?

解读

在 Rust 的模式匹配(match、if let、while let、函数参数、let 绑定等)中,“_” 是“忽略单个值”的通配符“..” 是“忽略剩余所有字段/元素”的剩余通配符
面试官问“优先级”,并不是问运算符优先级,而是问:当两者同时出现在同一个模式时,编译器以谁为准?会不会冲突?
国内面试里,这道题常作为“模式匹配边界 case”出现,用来快速判断候选人是否真写过复杂解构,还是只写过教程级的 match。

知识点

  1. “_” 只能绑定零次,它不会把值 move 进任何变量,因此不占用所有权
  2. “..” 只能出现在结构体、元组、元组结构体、数组、切片、enum 变体的模式里,并且最多出现一次;它把“剩余字段”整体忽略,同样不 move 任何子值
  3. Rust 2018 之后,编译器在“同一层级”里对“_”与“..”做“互补检查”
    • 如果“..”已经把所有未显式列出的字段“吃掉”,那么再写“_”就会编译错误(E0027)。
    • 反之,如果先写了“_”去忽略某个字段,再写“..”也会报错(E0027)。
      一句话:在同一层级,两者互斥,没有“谁优先级高”一说,编译器直接拒绝歧义
  4. 嵌套场景下,两者可以共存,只要不在同一层级即可通过检查。
  5. 版本差异:Rust 1.20 之前曾允许“_ , ..”同时出现,但 2018 edition 起全部拒绝;面试时如果提到“老版本可以”会加分,但必须强调现行规则是“互斥”

答案

同一模式层级里,“_” 与 “..” 互斥,编译器直接报错,不存在谁优先
如果必须忽略单个字段,就用“_”显式点名;如果想忽略其余所有字段,就用“..”且最多一次;两者不要同时出现在同一层。
记住编译器错误码 E0027 就是面试官想听的关键词。

拓展思考

  1. 实战陷阱:在 FFI 解构大结构体时,为了向后兼容,常想写
    let Foo { a, b, .. } = foo;
    如果后续版本新增字段 c: NonCopy,而代码里又补了一行 _ => {} 分支,就会触发 E0027。
    正确做法:只用“..”一次,永远不要补“_”字段。
  2. 宏生成代码:宏里动态拼模式时,务必用 quote!.. 计数器保证最多一次,否则下游用户展开即报错。
  3. 面试加分回答
    “如果团队真想表达‘先忽略某个字段,再忽略其余’,可以把模式拆成两级:先 let Foo { a, .. } = foo; 取出 a,再对 a 内部用 _ 忽略子字段,这样既符合所有权规则,也避开 E0027。”