rustc 错误码 E0431 深度解析:`use {self}` 为何"不能把当前模块导入自身",以及它为何已退役
本指南以 rustc 错误码文档 E0431.md 为骨架,讲解曾经用于诊断"非法 self 导入"的编译错误 E0431:它针对 use {self}; 这类把当前模块导入自身的裸 self 导入,要求 self 只能出现在"带非空前缀的导入列表"中。文章将完整还原该错误码的语义、修复思路与文档现状,并结合 rustc_resolve 的解析实现与 rustc_error_codes 的维护机制,说明 self 在 use 路径中的位置规则、错误码"退役"后的处理约定,以及与 E0430/E0432/E0433 等相邻错误码的分工。读完你既能理解 use ...::{self, ...} 与裸 use {self}; 的本质差异,也能掌握 rustc 错误码文档的更新规范。
E0431 说了什么:文档现状与原始报错
在 rustc 的"错误码即文档"体系里,每个错误码都对应 error_codes 目录下的一个 EXXXX.md 文件。E0431 的文档第一行就明确标注:
Note: this error code is no longer emitted by the compiler.
也就是说,在当前的 rustc 版本中,编译器已经不再产生 E0431 这个错误码,该文档保留下来主要是历史档案与维护参考用途。文档随之给出了它的历史含义与错误样例。
历史错误语义
E0431 描述的错误是:做出了一次非法的 self 导入(An invalid self import was made)。触发该错误的最小代码如下:
use {self}; // error: `self` import can only appear in an import list with a
// non-empty prefix
注意代码块上的标注 ignore (error is no longer emitted)——这是 rustc_error_codes 的约定:对于已经停发的错误码,文档中的代码示例不再参与构建(ignore),并显式注明该错误已不再被编译器发出。原始报错信息是:
selfimport can only appear in an import list with a non-empty prefix (self导入只能出现在带非空前缀的导入列表中)
文档给出的修复建议
原文档对该错误的处理建议非常直白:你无法把当前模块导入到它自身。若代码中出现这样的导入,要么直接删除它,要么核对一下是不是把其他名字误拼写成了 self:
You cannot import the current module into itself, please remove this import or verify you didn't misspell it.
也就是说,use {self}; 在历史上被视为"导入当前模块自身"这一无意义甚至循环的操作,属于写错(通常是本意想写某个真实模块/别名,却落笔成了 self)。
什么是"带非空前缀的导入列表"中的 self
要理解 E0431,关键在于解析 use 导入列表的书写形态。use 语句的花括号列表里允许出现 self,例如:
use something::{self, foo}; // `self` 前缀非空:something::{self, ...} 是合法的
这里的 self 指代的是导入列表中路径前缀(something)对应的那个模块自身,其效果等价于把 something 这个名字带进作用域。正因为列表外侧存在非空前缀 something,self 才有明确的指代对象。
而 E0431 针对的 use {self}; 则是把花括号列表直接挂在顶层、前缀为空:
use {self}; // 前缀为空:`self` 无法对应到任何"当前列表前缀",等价于导入当前模块自身
在没有前缀可依附的情况下,self 导入便失去了意义,于是在历史上被编译器拒绝并报出 E0431。这就是报错文案中 "can only appear in an import list with a non-empty prefix" 的确切含义——self 只有出现在形如 something::{self} 的"非空前缀列表"中才被允许。
从解析器源码看 self 在 use 路径中的真实行为
E0431 虽已停发,但 self 在 use 路径中的位置规则至今仍由名称解析器(resolver)严格执行。相关实现位于 build_reduced_graph.rs,函数 build_reduced_graph_for_use_tree 负责把一棵 use 语法树(use tree)展开为具体的导入记录。
前缀的构造与 2015 版次语义
解析器先由"父前缀 + 当前 use tree 自身前缀"拼出完整前缀(L611-L615)。值得留意的是 Rust 2015 edition 的默认解析规则(L617-L637):2015 版下 use 中的路径默认相对 crate 根解析,因此当首个非空段不是路径关键字时,会惰性地在开头补一个 crate 根段;因为前缀是"惰性"补充的,花括号列表内部的各个导入可以各自独立地获得根前缀——这正是后续能区分 use a::{self} 与裸 use {self} 的语法基础。
尾部 self 会被"替换成父标识"
对于单个简单导入(L643-L656),解析器把最后一个路径段作为导入源。其中有一段关键逻辑:当导入源标识是 self 且没有改名(rename)时,它会被替换为父前缀最后一段的标识名(前提是父前缀非空):
// 若标识为 `self` 且未重命名,则替换为父级标识
let ident = if source.ident.name == kw::SelfLower
&& rename.is_none()
&& let Some(parent) = module_path.last()
{
Ident::new(parent.ident.name, source.ident.span)
} else {
use_tree.ident()
};
这就从实现层面印证了 E0431 的历史约束:self 之所以"只能出现在带非空前缀的列表中",是因为它的真正语义是取前缀最后一个段的名字。例如 use something::{self}; 会被转换为一个名字为 something 的导入;而裸写 use {self}; 时前缀为空、没有任何父标识可替换,self 便无从落地,因而在历史上被判定为"把当前模块导入自身"的非法写法而报 E0431。
self 与 super、crate 等关键字的位置约束
在关键字的位置合法性上,当前解析器仍保留着一系列显式拒绝(L658-L726):
crate、$crate只能出现在路径起始位置;super只能出现在起始位置、self之后或另一个super之后;use ::{self};(self挂在路径根下、且根不是 crate 根)会被拒绝,报 "extern prelude cannot be imported";self若出现在use ...::self::source这种中间位置也会被拒绝,报 "selfin paths can only be used in start position or last position"(L705-L716);- 未改名的路径关键字(如
self不带as)不允许作为未命名导入,会触发UnnamedImport诊断(L718-L726)。
可以看到,虽然 E0431 这一具体错误码已不再产出,但 "关键字 self 必须在 use 路径的合适位置出现"这一设计原则始终保留,只是改由上述若干更细粒度、信息更丰富的诊断来兜底覆盖。
rustc 错误码文档体系与"退役"维护约定
E0431 的停发不是简单地把文档删掉了事。rustc 把全部错误码集中登记在 lib.rs 顶部的 error_codes! 高阶宏中,该宏随后会被 rustc_errors 等 crate 消费,用于注册与渲染错误说明。在 lib.rs 中可以看到 0430、0431、0432 等 E04 段错误码仍保留在注册列表里:
0430,
0431,
0432,
宏上方的维护注释(lib.rs L21-L24)对此有明确规定:
Do not remove entries from this list. Instead, just add a note to the corresponding markdown file saying that this error is not emitted by the compiler any more ... and remove all code examples that do not build any more by marking them with
ignore (no longer emitted).
翻译过来就是三条硬性约定:
- 不要从注册列表中删除条目——即使某个错误码不再被编译器发出,也要保留编号,防止历史编号错位、文档失联;
- 在对应的
EXXXX.md文档开头加上 "this error code is no longer emitted by the compiler" 之类的说明; - 对无法再构建通过的代码示例,统一标注
ignore (no longer emitted)。
此外,仓库的 tidy 检查(注释中提到的 check_error_codes_docs)会校验这些文档与宏列表的一致性,确保错误码文档的增删改符合规范。E0431.md 正是上述流程的完整范例:条目编号保留在宏中,正文注明停发,示例以 ignore 标注——这样既维护了历史档案,又不会干扰当前编译器测试套件的构建。
相邻错误码:E0430、E0432 与 E0433 的分工
E0431 属于 E04xx"导入/解析"错误族,它与相邻的几个错误码共同覆盖 use 语句的各类问题,了解它们有助于排查时快速对号入座:
E0430:列表中 self 重复出现
E0430.md 同样标注"不再由编译器发出",针对的是 self 在列表中重复的问题:
use something::{self, self}; // error: `self` import can only appear once in the list
文档给出的正确写法是去掉重复项,保留单个 use something::{self};。可以看出,E0430 与 E0431 是一对"孪生"约束:一个管 self 出现次数不能超过一次,另一个管 self 出现时前缀不能为空。
E0432:导入无法解析
E0432.md 目前仍在生效,负责"未解析的导入"(unresolved import)。它同时说明了 Rust 2015 与 2018+ 在 use 路径解析上的关键差异:
- Rust 2015:
use中的路径相对 crate 根解析,需要借助self::、super::前缀引用当前模块与父模块; - Rust 2018+:
use中的路径相对当前模块解析,除非以 crate 名或crate::开头; - 2015 版访问外部 crate 还需要显式
extern crate core;,2018+ 则直接use core::any;即可。
排查 E0432 时应核对:是否拼写错误、目标是否真的存在于源模块中、2015 版下是否漏写了 extern crate。
E0433:使用了未声明的 crate / 模块 / 类型
E0433.md 也是现行错误码,与 E0432 的差别在于:E0432 是针对 use 语句本身的路径解析失败,而 E0433 针对的是在表达式/类型位置使用了一个从未声明、从未导入的名字,例如 let map = HashMap::new(); 而忘记 use std::collections::HashMap;。若是外部 crate,还需确认依赖已写入 Cargo.toml;若是当前 crate 内的模块,则应加上 crate:: 前缀。
简单概括:E0430 管"重复 self",E0431(已停发)管"裸 self 导入",E0432 管"use 路径找不到目标",E0433 管"使用处根本没有这个名字"。
实践建议与小结
对普通 Rust 开发者而言,遇到 E0431 的实际概率已经为零(当前编译器不再发出该错误码)。但理解它的历史语义仍有价值:
- 如果你在旧版本编译器或历史代码库中见到 E0431,处理方式很简单:删除那条裸
use {self};,或核对是否把真实的模块名误写成了self。若确需把某模块自身带入作用域,应写成带非空前缀的形式,如use something::{self};,或直接使用self::、super::、crate::等更明确的前缀。 - 若你是在做 rustc 编译器本身的维护或贡献,E0431.md 是一个很好的"退役错误码文档"范本:牢记 lib.rs 中的规则——保留宏中的编号、在文档首行标注不再发出、用
ignore (no longer emitted)标注失效示例,才能通过 tidy 的check_error_codes_docs校验并保持错误码体系的长期稳定。
总而言之,E0431 记录的是 Rust 名称解析历史上一条"把当前模块导入自身"的非法路径约束。如今它虽已退役,但通过文档与 build_reduced_graph.rs 中 self 的替换与位置检查逻辑的对照,我们依然能清晰还原出 rustc 对 self 关键字的语义定位:它是模块路径里的"自引用",只能在有明确前缀对象时出现——无论报错形式如何演变,这条设计内核始终没有改变。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00