E0735 错误代码解析:为什么结构体、枚举和联合体的类型参数默认值不能使用Self
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
本篇文章以 rustc 错误代码文档 E0735.md 为主体,结合编译器源码(
rustc_resolve名称解析模块)深入讲解该错误的触发场景、底层原理与修复方法。读者读完后将掌握 E0735 的完整语义、Self在泛型默认值中的合法使用边界,以及如何在实战中定位并修复此类编译错误。
错误概览
E0735 是 rustc 在名称解析(name resolution)阶段报告的一类编译错误,其官方描述为:
Type parameter defaults cannot use
Selfon structs, enums, or unions.(在结构体、枚举或联合体上,类型参数的默认值不能使用Self。)
当你在struct、enum、union的泛型参数默认值中引用Self时,编译器会拒绝这段代码。这是 Rust 对 ADT(Algebraic Data Type,代数数据类型)泛型默认值施加的一项明确限制,与 trait 中允许Self作为默认值(如trait Add<Rhs = Self>)的规则形成鲜明对比。
触发场景:一个典型的错误示例
错误代码文档给出了最直接的复现代码:
struct Foo<X = Box<Self>> { field1: Option<X>, field2: Option<X>, } // error: type parameters cannot use `Self` in their defaults.这里Foo<X = Box<Self>>试图把类型参数X的默认值设为Box<Self>——即"一个装着 Foo 自身的盒子"。从直觉上看,这似乎是想表达某种递归或自引用的类型结构,但 rustc 会直接拒绝:type parameters cannot useSelfin their defaults(类型参数不能在默认值中使用Self)。
编译时你看到的完整报错如下(由 diagnostics/mod.rs 中的诊断定义生成):
error[E0735]: type parameters cannot use `Self` in their defaults --> src/main.rs:1:20 | 1 | struct Foo<X = Box<Self>> { | ^^^^为什么禁止:从源码看Self的本质
要理解这条限制,需要先明白一个关键事实:Self本质上就是另一个类型参数。在 ADT 的泛型列表处理过程中,Self只有在所有类型参数都被提供之后才是"良定义"的。这一点在 late.rs 的源码注释中说得非常直白:
// rust-lang/rust#61631: The type `Self` is essentially // another type parameter. For ADTs, we consider it // well-defined only after all of the ADT type parameters have // been provided. Therefore, we do not allow use of `Self` // anywhere in ADT type parameter defaults.对应 Rust 社区 issue rust-lang/rust#61631,其逻辑是:
- 在解析 ADT 的类型参数默认值时,
GenericArgs(泛型实参列表)只能按顺序提供已经声明过的前面的类型参数; - 而
Self相当于排在所有参数之后的隐式参数,只有当全部显式类型参数都确定后它才有确定含义; - 因此在默认值中引用
Self会产生"尚未定义就使用"的前向引用(forward reference)问题。
同样的源码注释还强调了这条禁令的适用范围:
// (We however cannot ban `Self` for defaults on *all* generic // lists; e.g. trait generics can usefully refer to `Self`, // such as in the case of `trait Add<Rhs = Self>`.)也就是说,trait 的泛型默认值可以合法使用Self,例如标准库中trait Add<Rhs = Self>就是常见范式;禁令只针对 ADT。
底层实现:ForwardGenericParamBan机制
E0735 的检查发生在 rustc 名称解析阶段(rustc_resolvecrate)的 late resolution 中。核心机制是一个名为ForwardGenericParamBan(前向泛型参数禁令)的 Rib。
1. 建立禁令列表
在 late.rs 的visit_generic_params中,编译器把当前泛型列表里的所有参数先放入"禁令 Rib",随后逐个移除已处理完的参数:
fn visit_generic_params(&mut self, params: &'ast [GenericParam], add_self_upper: bool) { // For type parameter defaults, we have to ban access // to following type parameters, as the GenericArgs can only // provide previous type parameters as they're built. We // put all the parameters on the ban list and then remove // them one by one as they are processed and become available. let mut forward_ty_ban_rib = Rib::new(RibKind::ForwardGenericParamBan(ForwardGenericParamBanReason::Default)); let mut forward_const_ban_rib = Rib::new(RibKind::ForwardGenericParamBan(ForwardGenericParamBanReason::Default)); for param in params.iter() { match param.kind { GenericParamKind::Type { .. } => { forward_ty_ban_rib .bindings .insert(Ident::with_dummy_span(param.ident.name), Res::Err); } GenericParamKind::Const { .. } => { forward_const_ban_rib .bindings .insert(Ident::with_dummy_span(param.ident.name), Res::Err); } GenericParamKind::Lifetime => {} } } ... }注意其中关键的一步——只有传入add_self_upper参数为true时,Self(kw::SelfUpper)才会被加入禁令列表:
if add_self_upper { // (`Some` if + only if we are in ADT's generics.) forward_ty_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err); }2. 何时对Self启用禁令
add_self_upper的取值取决于当前是否处于 ADT 的泛型列表中,见 late.rs:
fn visit_generics(&mut self, generics: &'ast Generics) { self.visit_generic_params(&generics.params, self.diag_metadata.current_self_item.is_some()); for p in &generics.where_clause.predicates { self.visit_where_predicate(p); } }只有当current_self_item.is_some()(即当前正在处理结构体、枚举、联合体等拥有Self的项的泛型)时,Self才进入禁令 Rib。这也解释了为什么 trait 泛型中Self合法——trait 场景下该路径不会把Self放入禁令列表。
3. 命中禁令后的诊断
当解析器在禁令 Rib 中解析到Self时,ident.rs 的validate_res_from_ribs会报告对应的解析错误:
// An invalid forward use of a generic parameter from a previous default // or in a const param ty. if let RibKind::ForwardGenericParamBan(reason) = all_ribs[rib_index].kind { if let Some(span) = finalize { let res_error = if rib_ident.name == kw::SelfUpper { ResolutionError::ForwardDeclaredSelf(reason) } else { ResolutionError::ForwardDeclaredGenericParam(rib_ident.name, reason) }; self.report_error(span, res_error); } assert_eq!(res, Res::Err); return Res::Err; }随后在 diagnostics/impls.rs 中,ForwardDeclaredSelf按原因分派到具体诊断。当ForwardGenericParamBanReason为Default(即类型参数默认值场景)时,生成 E0735:
ResolutionError::ForwardDeclaredSelf(reason) => match reason { ForwardGenericParamBanReason::Default => { self.dcx().create_err(diagnostics::SelfInGenericParamDefault { span }) } ForwardGenericParamBanReason::ConstParamTy => self .dcx() .create_err(diagnostics::SelfInConstGenericTy { span, enable_feature: false }), },而诊断本身在 diagnostics/mod.rs 中定义,最终呈现的报错文本即:
#[derive(Diagnostic)] #[diag("generic parameters cannot use `Self` in their defaults", code = E0735)] pub(crate) struct SelfInGenericParamDefault { #[primary_span] pub(crate) span: Span, }值得一提:同一个ForwardGenericParamBan机制还承担着另一类前向引用检查——当原因变为ConstParamTy时(const 参数类型引用Self),会触发另一条诊断(不允许在 const 参数类型中使用Self,在开启min_adt_const_params特性后有对应的放宽路径)。可见 E0735 属于"泛型默认值前向引用禁令"体系中的一个分支。
如何修复
修复 E0735 的核心思路是:不要在 ADT 类型参数默认值中引用Self,改用具名的具体类型。
方案一:用具体类型替换Self
// 错误:默认值引用 Self struct Foo<X = Box<Self>> { field1: Option<X>, field2: Option<X>, } // 修复:使用明确的具体类型作为默认值 struct Foo<X = Box<Foo>> { field1: Option<X>, field2: Option<X>, }注意这里把Self替换成了类型名Foo本身,编译即可通过。之所以能这样写,是因为在 ADT 的类型参数默认值中,使用该 ADT 自身的具名类型是允许的——禁令只针对Self这个关键字形态。
方案二:去掉默认值,改用显式泛型参数
如果Box<Self>只是示例,实际并不需要默认值,直接移除默认值声明即可:
struct Foo<X> { field1: Option<X>, field2: Option<X>, }调用时显式指定类型参数,例如Foo<Box<Foo>>。
方案三:把"自引用默认值"的需求转移到 trait 上
如果确实需要"默认情况下引用自身"的语义,可以考虑用 trait 表达,因为trait 的类型参数默认值允许Self:
trait Wrapper<X = Box<Self>> { fn wrap(&self) -> X; } struct Foo; impl Wrapper for Foo { fn wrap(&self) -> Box<Foo> { Box::new(Foo) } }这与trait Add<Rhs = Self>的写法一脉相承,是标准库也采用的做法。
合法与非法用法对照
| 场景 | 写法 | 是否触发 E0735 |
|---|---|---|
结构体类型参数默认值引用Self | struct Foo<X = Box<Self>> | ✅ 触发 |
枚举类型参数默认值引用Self | enum E<X = Option<Self>> | ✅ 触发 |
联合体类型参数默认值引用Self | union U<X = ManuallyDrop<Self>> | ✅ 触发 |
trait 类型参数默认值引用Self | trait Add<Rhs = Self> | ❌ 合法 |
| 结构体默认值引用自身具名类型 | struct Foo<X = Box<Foo>> | ❌ 合法 |
| 默认值引用前面已声明的参数 | struct Foo<A, B = Option<A>> | ❌ 合法 |
| 默认值引用后面未声明的参数 | struct Foo<A = B, B> | ✅ 触发同类前向引用错误 |
上表最后一行对应的是同机制下的另一条错误(前向引用后续泛型参数,ForwardDeclaredGenericParam),说明这套禁令不只是针对Self,而是面向所有"默认值中引用尚未就绪参数"的情况。
小结
E0735 是 rustc 名称解析阶段对 ADT 泛型默认值的一项防御性检查,其背后是对Self作为"隐式尾随类型参数"语义的严谨约束:在结构体、枚举、联合体的泛型默认值中,Self尚未被完整定义,因此被明确禁止;而 trait 泛型默认值则可以自由使用Self。理解这一点,不仅能快速定位修复此类编译错误,也能更深入地理解 Rust 泛型系统与名称解析(ForwardGenericParamBanRib 机制)的设计细节。
相关仓库路径速查
- 错误文档:compiler/rustc_error_codes/src/error_codes/E0735.md
- 诊断定义:compiler/rustc_resolve/src/diagnostics/mod.rs
- 诊断生成与分派:compiler/rustc_resolve/src/diagnostics/impls.rs
- 禁令 Rib 的建立与
Self处理:compiler/rustc_resolve/src/late.rs - 解析命中禁令与报错:compiler/rustc_resolve/src/ident.rs
【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考