通配符 _ 与 .. 的优先级?
解读
在 Rust 的模式匹配(match、if let、while let、函数参数、let 绑定等)中,“_” 是“忽略单个值”的通配符,“..” 是“忽略剩余所有字段/元素”的剩余通配符。
面试官问“优先级”,并不是问运算符优先级,而是问:当两者同时出现在同一个模式时,编译器以谁为准?会不会冲突?
国内面试里,这道题常作为“模式匹配边界 case”出现,用来快速判断候选人是否真写过复杂解构,还是只写过教程级的 match。
知识点
- “_” 只能绑定零次,它不会把值 move 进任何变量,因此不占用所有权。
- “..” 只能出现在结构体、元组、元组结构体、数组、切片、enum 变体的模式里,并且最多出现一次;它把“剩余字段”整体忽略,同样不 move 任何子值。
- Rust 2018 之后,编译器在“同一层级”里对“_”与“..”做“互补检查”:
- 如果“..”已经把所有未显式列出的字段“吃掉”,那么再写“_”就会编译错误(E0027)。
- 反之,如果先写了“_”去忽略某个字段,再写“..”也会报错(E0027)。
一句话:在同一层级,两者互斥,没有“谁优先级高”一说,编译器直接拒绝歧义。
- 嵌套场景下,两者可以共存,只要不在同一层级即可通过检查。
- 版本差异:Rust 1.20 之前曾允许“_ , ..”同时出现,但 2018 edition 起全部拒绝;面试时如果提到“老版本可以”会加分,但必须强调现行规则是“互斥”。
答案
在同一模式层级里,“_” 与 “..” 互斥,编译器直接报错,不存在谁优先。
如果必须忽略单个字段,就用“_”显式点名;如果想忽略其余所有字段,就用“..”且最多一次;两者不要同时出现在同一层。
记住编译器错误码 E0027 就是面试官想听的关键词。
拓展思考
- 实战陷阱:在 FFI 解构大结构体时,为了向后兼容,常想写
let Foo { a, b, .. } = foo;
如果后续版本新增字段c: NonCopy,而代码里又补了一行_ => {}分支,就会触发 E0027。
正确做法:只用“..”一次,永远不要补“_”字段。 - 宏生成代码:宏里动态拼模式时,务必用
quote!的..计数器保证最多一次,否则下游用户展开即报错。 - 面试加分回答:
“如果团队真想表达‘先忽略某个字段,再忽略其余’,可以把模式拆成两级:先let Foo { a, .. } = foo;取出a,再对a内部用_忽略子字段,这样既符合所有权规则,也避开 E0027。”