ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

E0735 错误代码解析:为什么结构体、枚举和联合体的类型参数默认值不能使用 `Self`

E0735 错误代码解析:为什么结构体、枚举和联合体的类型参数默认值不能使用 `Self` 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 useSelfon structs, enums, or unions.在结构体、枚举或联合体上类型参数的默认值不能使用Self。当你在struct、enum、union的泛型参数默认值中引用Self时编译器会拒绝这段代码。这是 Rust 对 ADTAlgebraic Data Type代数数据类型泛型默认值施加的一项明确限制与 trait 中允许Self作为默认值如trait AddRhs Self的规则形成鲜明对比。触发场景一个典型的错误示例错误代码文档给出了最直接的复现代码struct FooX BoxSelf { field1: OptionX, field2: OptionX, } // error: type parameters cannot use Self in their defaults.这里FooX BoxSelf试图把类型参数X的默认值设为BoxSelf——即一个装着 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 FooX BoxSelf { | ^^^^为什么禁止从源码看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 AddRhs Self.)也就是说trait 的泛型默认值可以合法使用Self例如标准库中trait AddRhs 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 theyre 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时Selfkw::SelfUpper才会被加入禁令列表if add_self_upper { // (Some if only if we are in ADTs generics.) forward_ty_ban_rib.bindings.insert(Ident::with_dummy_span(kw::SelfUpper), Res::Err); }2. 何时对Self启用禁令add_self_upper的取值取决于当前是否处于 ADT 的泛型列表中见 late.rsfn 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即类型参数默认值场景时生成 E0735ResolutionError::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 FooX BoxSelf { field1: OptionX, field2: OptionX, } // 修复使用明确的具体类型作为默认值 struct FooX BoxFoo { field1: OptionX, field2: OptionX, }注意这里把Self替换成了类型名Foo本身编译即可通过。之所以能这样写是因为在 ADT 的类型参数默认值中使用该 ADT 自身的具名类型是允许的——禁令只针对Self这个关键字形态。方案二去掉默认值改用显式泛型参数如果BoxSelf只是示例实际并不需要默认值直接移除默认值声明即可struct FooX { field1: OptionX, field2: OptionX, }调用时显式指定类型参数例如FooBoxFoo。方案三把自引用默认值的需求转移到 trait 上如果确实需要默认情况下引用自身的语义可以考虑用 trait 表达因为trait 的类型参数默认值允许Selftrait WrapperX BoxSelf { fn wrap(self) - X; } struct Foo; impl Wrapper for Foo { fn wrap(self) - BoxFoo { Box::new(Foo) } }这与trait AddRhs Self的写法一脉相承是标准库也采用的做法。合法与非法用法对照场景写法是否触发 E0735结构体类型参数默认值引用Selfstruct FooX BoxSelf✅ 触发枚举类型参数默认值引用Selfenum EX OptionSelf✅ 触发联合体类型参数默认值引用Selfunion UX ManuallyDropSelf✅ 触发trait 类型参数默认值引用Selftrait AddRhs Self❌ 合法结构体默认值引用自身具名类型struct FooX BoxFoo❌ 合法默认值引用前面已声明的参数struct FooA, B OptionA❌ 合法默认值引用后面未声明的参数struct FooA 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),仅供参考
返回列表