Rust E0106 深度解析:missing lifetime specifier 错误的触发场景、省略规则与 rustc 源码实现
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
E0106 是 Rust 编译器在类型或函数签名中缺少生命周期标注(missing lifetime specifier)时报出的错误,它既覆盖“结构体/枚举/类型别名里的引用没写生命周期”,也覆盖“函数签名不符合生命周期省略(elision)规则”两类场景。本文以 Rust 官方的错误码解释文档为核心,结合 rustc 源码中诊断的生成位置,完整讲解该错误的触发规则、修复方式,以及rustc_resolve是如何定位并给出修复建议的。
E0106 是什么
E0106 的定义来自 E0106.md:
This error indicates that a lifetime is missing from a type. If it is an error inside a function signature, the problem may be with failing to adhere to the lifetime elision rules (see below).
也就是说,该错误有两种典型来源:
- 类型定义中遗漏生命周期:结构体、枚举、类型别名等“非函数”上下文中出现的引用(如
&bool、&str)必须显式携带生命周期,编译器不会为它们做任何省略推断; - 函数签名违反省略规则:函数签名中的引用允许省略生命周期,但省略是受严格约束的有限推断,一旦不满足三条省略规则,同样会触发 E0106。
在错误码体系中,E0106 被登记在 error_codes! 宏列表 中(第 85 行附近的0106条目),该宏注释明确要求每个错误码对应一个error_codes/EXXXX.md解释文件,并受 tidy 的check_error_codes_docs检查约束——这正是本文所依据的文档存在的位置与格式规范。
场景一:类型定义中缺少生命周期参数
原文档给出的第一组示例覆盖了结构体、枚举、类型别名三种声明形式:
struct Foo1 { x: &bool } // ^ expected lifetime parameter struct Foo2<'a> { x: &'a bool } // correct struct Bar1 { x: Foo2 } // ^^^^ expected lifetime parameter struct Bar2<'a> { x: Foo2<'a> } // correct enum Baz1 { A(u8), B(&bool), } // ^ expected lifetime parameter enum Baz2<'a> { A(u8), B(&'a bool), } // correct type MyStr1 = &str; // ^ expected lifetime parameter type MyStr2<'a> = &'a str; // correct这组代码体现了三条规律:
- 直接包含引用的类型(
Foo1、Baz1、MyStr1):引用本身缺生命周期,错误指向&记号; - 引用了带生命周期参数的类型(
Bar1):Foo2声明为Foo2<'a>,使用时必须传入生命周期实参,Foo2后面的位置就是编译器提示“expected lifetime parameter”的地方,错误指向类型名本身; - 正确写法统一遵循一个原则:声明处添加生命周期参数(
<'a>),使用处传入对应的生命周期实参。
从源码结构看,编译器对“缺失的生命周期”并非只有一种记录方式。MissingLifetimeKind 枚举区分了四种缺失形态:
pub enum MissingLifetimeKind { /// An explicit `'_`. Underscore, /// An elided lifetime `&' ty`. Ampersand, /// An elided lifetime in brackets with written brackets. Comma, /// An elided lifetime with elided brackets. Brackets, }即显式写'_但位置不合法(Underscore)、&后完全省略(Ampersand)、括号内省略(Comma)、连括号都省略(Brackets)分别对应不同的诊断建议生成路径——这是后文修复建议能“贴合现场”的前提。
场景二:函数签名违反生命周期省略规则
原文档第二部分解释了 lifetime elision(生命周期省略):它是函数签名中一种“特殊的、受限的”生命周期推断机制,允许在特定情况下省略生命周期标注。其规则要求:任何返回生命周期被省略的函数签名,必须满足以下二者之一:
- 恰好只有一个输入生命周期(the output lifetime is inferred to be the same as the unique input lifetime);
- 或者有多个输入生命周期,但该函数必须是方法,且接收者为
&self或&mut self(此时输出生命周期被推断为与self的引用生命周期相同)。
文档同时给出了三类违反省略规则的最小示例:
// error, no input lifetimes fn foo() -> &str { } // error, `x` and `y` have distinct lifetimes inferred fn bar(x: &str, y: &str) -> &str { } // error, `y`'s lifetime is inferred to be distinct from `x`'s fn baz<'a>(x: &'a str, y: &str) -> &str { }逐条理解:
foo:没有任何输入生命周期,返回的&str无从推断——借用自谁都不存在;bar:x与y被推断为相互独立的生命周期,输出无法确定借用自哪一个;baz:虽然x显式标注了'a,但y的省略生命周期被推断为与'a不同,因此候选仍然不唯一。
对应的修复方式是显式标注返回类型的生命周期,例如把bar改为fn bar(x: &str, y: &str) -> &str { x }这类情况需要显式写fn bar<'a>(x: &'a str, y: &str) -> &'a str,明确“返回值借用自x”。
源码溯源:E0106 诊断在哪里生成
E0106 的报错发生在名称解析的 late 阶段。rustc_resolve 的 late 解析器 维护一个current_elision_failures: Vec<(MissingLifetime, Either<NodeId, Range<NodeId>>)>字段(约 L805),在解析函数参数与返回类型时逐条记录“缺失的生命周期 + 相关节点”;解析完成后(约 L2488-L2507)取出这批失败记录并触发诊断。
真正组装错误信息的函数是 report_missing_lifetime_specifiers:
let mut err = struct_span_code_err!( self.r.dcx(), spans, E0106, "missing lifetime specifier{}", pluralize!(num_lifetimes) );主诊断标题即missing lifetime specifier,并对每个缺失位置附加expected named lifetime parameter标签(多个时显示数量,如expected 2 lifetime parameters)——这正是文档示例中// ^ expected lifetime parameter注释的由来。
该诊断的 help 分支(diagnostics.rs)与省略规则一一对应,可以据此反推编译器的判定逻辑:
| 现场条件 | 编译器给出的 help |
|---|---|
| 函数参数中不存在任何被省略生命周期 | “this function's return type contains a borrowed value, but there is no value for it to be borrowed from” |
| 存在被省略的生命周期,但不满足省略规则 | “…but the lifetime cannot be derived from the arguments” |
| 候选输入生命周期恰好 1 个 | “…but the signature does not say whichxit is borrowed from” |
| 候选输入生命周期多于 1 个 | “…but the signature does not say whether it is borrowed fromxory” |
此外,诊断还会根据当前作用域内已有的生命周期名称选择建议名(diagnostics.rs):作用域为空时建议'a,已有单个生命周期时复用其名称,否则建议'lifetime;并按MissingLifetimeKind生成不同的替换建议(对&补&'a、对'_直接替换、对带括号的位置插入逗号分隔的多个生命周期等),这就是 rustc 能给出“可直接套用”修复建议的原因。
修复清单
结合文档示例与源码行为,修复 E0106 时可以按以下顺序操作:
- 结构体/枚举/类型别名:在声明处添加生命周期参数,在引用该类型的地方传入实参,对照前文
Foo2/Bar2/Baz2/MyStr2的写法; - 无参数函数返回引用:为返回类型指定
'static或其他可推导的生命周期;若引用来自参数,为签名补充生命周期参数; - 多参数函数返回引用:显式写出返回生命周期,并让它与某个参数的生命周期同名,明确“借用自哪一个输入”;方法场景可依赖
&self省略规则; - 复用编译器建议:rustc 的
missing lifetime specifier诊断通常自带带具体名称的替换建议('_或补&'a),优先采纳建议、再结合语义确认借用来源是否正确。
参考位置
- 错误码解释文档:E0106.md
- 错误码登记表:rustc_error_codes/src/lib.rs
- 诊断生成实现:rustc_resolve/src/late/diagnostics.rs、rustc_resolve/src/late.rs
- 缺失生命周期分类:MissingLifetimeKind
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考