news 2026/9/11 6:02:13

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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。)

当你在structenumunion的泛型参数默认值中引用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,其逻辑是:

  1. 在解析 ADT 的类型参数默认值时,GenericArgs(泛型实参列表)只能按顺序提供已经声明过的前面的类型参数;
  2. Self相当于排在所有参数之后的隐式参数,只有当全部显式类型参数都确定后它才有确定含义;
  3. 因此在默认值中引用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时,Selfkw::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按原因分派到具体诊断。当ForwardGenericParamBanReasonDefault(即类型参数默认值场景)时,生成 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
结构体类型参数默认值引用Selfstruct Foo<X = Box<Self>>✅ 触发
枚举类型参数默认值引用Selfenum E<X = Option<Self>>✅ 触发
联合体类型参数默认值引用Selfunion U<X = ManuallyDrop<Self>>✅ 触发
trait 类型参数默认值引用Selftrait 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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 6:01:35

物联网STM32图书馆管理系统:RFID借书、ESP8266透传与上位机联动实践

简介&#xff1a;面向毕业设计场景的物联网图书管理系统项目包&#xff0c;融合STM32嵌入式、RFID识别与Java后端技术&#xff0c;兼顾嵌入式开发与后台管理需求&#xff0c;解决书店图书库存实时更新与信息化管理问题&#xff0c;适合计算机、物联网专业学生用于课程设计或毕设…

作者头像 李华
网站建设 2026/9/11 5:57:23

JavaSE I/O体系核心解析:从字节流到序列化的工程实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 5:57:16

2026 AI生成PPT工具可编辑性深度测评:真·可编辑才是生产力核心

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 5:56:50

Windows上安装Claude Code全指南:从环境准备到排错

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华