
rustc 错误码解析E0428 —— 名称被重复定义的触发场景与编译原理【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust导读E0428 是 rustc 编译器在名称解析阶段name resolution报告的一类重复定义错误在同一个作用域、同一个命名空间中类型、模块等名称被定义了不止一次。本文以 compiler/rustc_error_codes/src/error_codes/E0428.md 为核心结合编译器源码 compiler/rustc_resolve/src/diagnostics/impls.rs 与测试用例讲清它的触发条件、报错格式、修复方法与底层判定逻辑让你既会改代码也看得懂编译器为什么这么报。错误概述同一作用域内名称被重复定义E0428 的官方一句话定义是A type or module has been defined more than once. 某个类型或模块被定义了不止一次。在真实编译输出中错误信息的第一行表现为error[E0428]: the name Bar is defined multiple times其本质是在同一个模块或块作用域、同一个命名空间内两个非 import 引入的声明使用了完全相同的名称。编译器不允许这种歧义存在因为它无法判断后续代码引用的是哪一个定义。Rust 名称解析的三类命名空间理解 E0428 必须先理解 Rust 的命名空间机制。从 compiler/rustc_resolve/src/diagnostics/impls.rs 的源码可见冲突诊断会依据命名空间描述旧绑定old binding的种类值命名空间ValueNS——诊断中描述为value如函数、常量、静态变量等宏命名空间MacroNS——诊断中描述为macro如macro_rules!定义的宏类型命名空间TypeNS——诊断中描述为type、module、trait、extern crate包括结构体、枚举、联合体、类型别名、trait、模块等。E0428 触发的前提是冲突的两个声明落入同一个命名空间。反之同一名称分布在不同命名空间是合法的——例如struct Foo {}与fn Foo() {}可以共存因为前者占用类型命名空间、后者占用值命名空间二者互不干扰。只有当「同类目」的声明重名时才会报 E0428。触发场景与报错示例最基本的触发重复定义结构体关联文档给出了最直接的反例compile_fail标记表明这是一个用于文档测试、预期编译失败的示例struct Bar; struct Bar; // error: duplicate definition of value Bar第一行定义了结构体Bar第二行又用相同的名称定义了一个同名结构体于是编译器在类型命名空间中检测到重复名称抛出 E0428。请注意示例注释中的措辞duplicate definition of value它沿用自关联文档原文指“该名称被重复定义”实际输出中会根据命名空间输出type、module等描述。同源场景枚举、trait、类型别名与模块凡是在类型命名空间中占据名称的声明遇到同名声明时都会触发 E0428enum Color { Red, Green } enum Color { Blue } // E0428Color 被重复定义 mod foo { pub fn f() {} } mod foo { pub fn g() {} } // E0428两个名为 foo 的模块 type Point (i32, i32); type Point (f64, f64); // E0428类型别名 Point 重复定义 trait Runnable { fn run(self); } trait Runnable { fn go(self); } // E0428trait Runnable 重复定义需要说明上述扩展示例都落在 E0428 覆盖的「类型/模块重复定义」范畴内具体到某一声明种类是否能与原声明共存可结合本文后续「判定逻辑」一节的源码分支理解。函数、常量之间的重名值命名空间重复虽然关联文档标题聚焦“类型或模块”但从 impls.rs 的代码分支看E0428 的选用条件是「冲突双方都不是用户可见的 import」分支本身并不限定命名空间因此值命名空间内的同名重复如两个同名函数、同名常量同样会落入同一套NameDefinedMultipleTime诊断流程此时输出中的 namespace 描述会变为 value namespace 的字样。关联文档示例注释中的 “duplicate definition of value” 也正与这类取值命名空间的措辞相呼应。如何修复关联文档给出了两条修复方向Please verify you didnt misspell the type/modules name or remove/rename the duplicated one. 请检查你是否拼错了类型/模块名或删除/重命名重复的那一个。方法一重命名其中一个声明struct Bar; struct Bar2; // ok!对第二个声明重新取一个独一无二的名字是最直接、最常见的处理方式。此例中第一个Bar被保留重复声明更名为Bar2后编译通过。方法二删除冗余的重复声明如果两处定义本身是误粘贴、误合并产生的完全相同的副本直接删除后出现的重复声明即可只保留一个。方法三核对拼写与引入来源E0428 的一个高频诱因是拼写错误或大小写不一致导致误以为“是两个名字”。例如本想定义struct UserProfile;却写成了struct Userprofile; struct UserProfile;虽然后者并不构成 E0428但反过来若两个声明连拼写都完全一致真正重名则务必先确认是不是重复粘贴、重复导入或代码合并冲突留下的残余再从上述两个方向中选择处理。实战修复对比修复前的完整失败片段struct Bar; struct Bar;对应编译输出详见 tests/ui/error-codes/E0428.stderrerror[E0428]: the name Bar is defined multiple times -- $DIR/E0428.rs:4:1 | LL | struct Bar; | ----------- previous definition of the type Bar here LL | struct Bar; | ^^^^^^^^^^^ Bar redefined here | note: Bar must be defined only once in the type namespace of this module error: aborting due to 1 previous error For more information about this error, try rustc --explain E0428.修复后struct Bar; struct Bar2; // ok! fn main() {}报错格式背后的诊断结构体E0428 的渲染由名称解析器中的诊断结构体NameDefinedMultipleTime驱动定义在 compiler/rustc_resolve/src/diagnostics/mod.rs。它与输出逐行对应主诊断#[diag(the name{$name}is defined multiple times)]——即第一行error[E0428]: the nameBaris defined multiple times#[note(...)]——即note: Bar must be defined only once in the {type|value|macro} namespace of this {module|block}其中的命名空间与容器名由调用方填充子诊断NameDefinedMultipleTimeLabel——命中新绑定后输出的… redefined here子诊断NameDefinedMultipleTimeOldBindingLabel——指向旧绑定位置输出previous definition of the {kind}{name}here或previous import of …。所以标准输出总是成对出现先在旧定义处标注 previous definition of ...再在新定义处高亮 redefined here最后用一行 note 说明约束“同一命名空间内只能定义一次”。这三条信息正是排查重复定义最直接的线索。底层实现resolve 阶段的冲突判定与错误码路由E0428 并不产生于类型检查或代码生成而是发生在名称解析阶段。核心入口是 compiler/rustc_resolve/src/diagnostics/impls.rs 的report_conflict方法其内部流程大致如下按源码位置排序若旧绑定old_binding的 span 起点晚于新绑定则交换两者确保始终“错误报在第二次出现的定义上”见 impls.rs确定容器与命名空间描述容器取父模块或 blockModuleKind::Def或ModuleKind::Block命名空间描述按ValueNS / MacroNS / TypeNS与具体DefKind映射为value / macro / extern crate / module / trait / type错误码路由随后按「是否 extern crate」「是否 import」组合选择具体错误码旧绑定新绑定命中错误码extern crateextern crateE0259任一方为 extern crate任一方为 extern crateE0254 / E0260非 import非 importE0428importimportE0252import非 import混搭E0255当冲突双方都不是用户可见的 import即都是模块内的真实定义时错误码即路由到 E0428。构造诊断report_conflict在 impls.rs 中通过create_err(NameDefinedMultipleTime { ... }).with_code(code)绑定错误码 E0428 并发出。这一套逻辑同时说明了为何同样的“重名”在不同语境下会得到不同错误码真正的“定义撞定义”归 E0428而“导入撞导入”“定义撞导入”则分别归属 E0252 与 E0255 等相邻错误码详见 impls.rs。测试验证compiletest UI 用例与文档 doctest本仓库对该错误既有文档级测试也有独立的 compiletest UI 测试文档示例E0428.md 中的代码块带compile_fail,E0428属性由 rustdoc 的 doctest 机制在编译文档时验证其确实以 E0428 失败UI 回归测试tests/ui/error-codes/E0428.rs 完整复现了最小触发用例struct Bar; //~ NOTE previous definition of the type Bar here struct Bar; //~ ERROR E0428 fn main () {}其中//~ ERROR E0428断言第二行必报 E0428//~ NOTE previous definition of the typeBarhere断言第一行会给出“previous definition”的旧绑定提示期望输出固化在 tests/ui/error-codes/E0428.stderr防止编译器改动导致该错误信息意外回归。相邻错误码快速辨析排查 E0428 时常常会遇到“同样是重复名称”但错误码不同的情况。依据 impls.rs 的路由逻辑可快速对号入座错误码典型场景E0428模块内真实定义非 import在同一个命名空间中重复出现如structstruct、同名模块等E0252两条use导入把同一个名称引入同一作用域E0255import 与本地定义混用导致名称冲突E0259一个名称同时被两个不同的extern crate绑定原名冲突E0260 / E0254涉及extern crate与 import / 重导出的名称冲突如果错误信息里的旧绑定标注是 “previous import of …”说明冲突源自use引入而非本地定义此时应转向 E0252/E0255 一族的处理思路而不是简单重命名本地声明。排查建议小结编译报 E0428 时先看 stderr 中的三行核心信息主标题里的名字、旧绑定位置的 “previous definition …”、新绑定位置的 “redefined here”确认重名的是哪两处用rustc --explain E0428或rustc --explain后跟任意错误码在终端查看该错误的官方解释与示例对照本文的命名空间说明判断是否真的“同类重名”——类型命名空间与值命名空间的同名不冲突依据冲突来源选择修复方式误粘贴则删除真需要两个不同实体则重命名其一如Bar/Bar2跨文件冲突则检查模块的use是否把同名定义并入了同一作用域。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考