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

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

2026-09-09 11:36:47作者:温玫谨Lighthearted

本篇文章以 rustc 错误代码文档 E0735.md 为主体,结合编译器源码(rustc_resolve 名称解析模块)深入讲解该错误的触发场景、底层原理与修复方法。读者读完后将掌握 E0735 的完整语义、Self 在泛型默认值中的合法使用边界,以及如何在实战中定位并修复此类编译错误。

错误概览

E0735 是 rustc 在名称解析(name resolution)阶段报告的一类编译错误,其官方描述为:

Type parameter defaults cannot use Self on 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 use Self in 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_resolve crate)的 late resolution 中。核心机制是一个名为 ForwardGenericParamBan(前向泛型参数禁令)的 Rib。

1. 建立禁令列表

late.rsvisit_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.rsvalidate_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
结构体类型参数默认值引用 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 泛型系统与名称解析(ForwardGenericParamBan Rib 机制)的设计细节。

相关仓库路径速查

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
docsdocs
暂无描述
Markdown
899
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
924
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
599
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
525
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
394