Rust 编译器错误 E0365 深度解析私有模块的公开重导出与可见性修复【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust导读E0365 是 rustc 在解析阶段rustc_resolve针对私有模块被pub use公开重导出这一可见性冲突产生的编译错误。本文以本仓库中官方错误文档 E0365.md 为主线结合编译器解析源码与 ui 测试用例讲清该错误的触发场景、两种典型报错文案、pub mod/受限可见性两类修复思路以及它与相邻错误 E0364 的边界帮助你理解 Rust 模块系统与可见性模型的底层判定逻辑。E0365 是什么一句话速览该错误码的官方说明为Private modules cannot be publicly re-exported.——即你通过pub use重导出的模块自身并不是pub的。它属于编译期硬错误hard error在名称解析name resolution阶段即被报告而不是 lint 警告或运行时问题。想要对外部用户可见被重导出的条目本身必须具备足以支撑该重导出的可见性否则 rustc 会直接拒绝生成二进制或库产物。触发 E0365 的最小复现示例E0365.md 中给出的错误示例是模块中包含pub条目、但模块自身为私有的场景mod foo { pub const X: u32 1; } pub use foo as foo2; fn main() {}这里foo模块本身未声明pub尽管其中的常量X是pub的尝试对模块本身执行pub use foo as foo2依然会触发 E0365。这与 E0365.md 的语义定位一致重导出对象是模块这一类型命名空间中的条目判断依据是被导出者自身的可见性。该示例同时也是仓库中回归测试文件 tests/ui/error-codes/E0365.rs 的主体保证真实编译行为与文档描述同步。错误输出的真实形态两种 E0365 文案通过 tests/ui/error-codes/E0365.stderr 可以看到上述代码实际产生的完整诊断error[E0365]: foo is only public within the crate, and cannot be re-exported outside -- E0365.rs:5:9 | LL | pub use foo as foo2; | ^^^^^^^^^^^ re-export of crate public foo | note: consider declaring type or module foo with pub注意报错文案并非文档中概括的 is private而是is only public within the crate。这说明 rustc 对可见性做了更细的建模E0365 在诊断层面对应两种消息{$ident} is private, and cannot be re-exported——针对完全私有的模块{$ident} is only public within the crate, and cannot be re-exported outside——针对仅 crate 内公开相当于pub(crate)语义的模块。这两条消息分别由诊断结构体CannotBeReexportedPrivateNS与CannotBeReexportedCratePublicNS定义可在 compiler/rustc_resolve/src/diagnostics/mod.rs#L804-L830 中看到含代码 E0365 与修复提示 note、以及re-export of private/crate public ...的 span 标注#[derive(Diagnostic)] #[diag({$ident} is private, and cannot be re-exported, code E0365)] #[note(consider declaring type or module {$ident} with pub)] pub(crate) struct CannotBeReexportedPrivateNS { ... } #[derive(Diagnostic)] #[diag({$ident} is only public within the crate, and cannot be re-exported outside, code E0365)] #[note(consider declaring type or module {$ident} with pub)] pub(crate) struct CannotBeReexportedCratePublicNS { ... }这也解释了为何文档中的顶层示例会命中crate public分支位于 crate 顶层is_top_level_module()的私有模块在 rustc 的可见性模型中被视作restricted 到 crate 自身的可见性详见下文源码级判定逻辑。何时报 is private 而非 only public within the crate判定来自 compiler/rustc_resolve/src/imports.rs#L1668-L1671let crate_private_reexport match decl.vis() { Visibility::Restricted(mod_id) if mod_id.is_top_level_module() true, _ false, };当被重导出模块的可见性被建模为restricted 到顶层模块时按 crate-public 处理输出 only public within the crate其他受限情形例如嵌套在私有父模块下、受限到某个具体模块则输出 is private。因此嵌套于私有模块内部的pub use通常命中 private 分支而直接声明在 crate 根下、仅未写pub的顶层模块通常命中 crate-public 分支。修复方法让模块本身公开官方推荐方案E0365.md 给出的标准修法非常直接——把被重导出的模块声明为pubpub mod foo { pub const X: u32 1; } pub use foo as foo2; fn main() {}由于foo现在对 crate 外部可见pub use foo as foo2的重导出链合法成立编译通过。rustc 的诊断输出中也会给出同义建议note: consider declaring type or modulefoowithpub。修复的变体为不同可见性场景选择合适写法在真实项目中修复方案并不只有加pub一种需依据你对模块的实际可见性意图来选择模块需对下游使用者公开并希望以别名重导出——将模块声明为pub mod foo后再pub use foo as foo2即上述方案模块仅在当前 crate 内部复用、不打算对外暴露——可把重导出连同模块本身都降为 crate 内可见例如pub(crate) use foo as foo2此时即便foo是pub(crate)/私有模块也不会报 E0365因为重导出的名义可见性并未超出被导出者的可见性只想让模块中的某个pub条目对外可见——不要重导出整个模块改为直接pub use foo::X并保持mod foo私有模块私有不阻止其内部pub条目被单独重导出前提是这些条目可达。说明E0365 触发的关键是比较重导出语句的可见性与被导出条目的可见性。若重导出方的名义可见性严格大于被导出条目例如pub重导出pub(crate)/私有模块即构成非法 re-export。这条比较逻辑见 imports.rs#L1619-L1634。源码级原理E0365 是在哪一步、如何被抛出的E0365 的产生地是rustc_resolve的名称解析对 import 的处理过程。可以从 compiler/rustc_resolve/src/imports.rs 的核心流程看到完整判定链条解析pub use时编译器按命名空间分别取得目标 bindingself.per_ns(...)逐命名空间比较import.vis与被导入项binding.vis()的可见性大小当import.vis.greater_than(binding.vis(), this.tcx)为真说明该重导出越权记录为 re-export error只有当所有由该 import 引入的声明都比 import 的名义可见性更私有时才真正发射错误对应注释 In isolation, a declaration like this is not an error, but ifall1-3 declarations introduced by the import are more private than the import items nominal visibility, then its an error.见 imports.rs#L1620-L1634实际发射由report_cannot_reexportimports.rs#L1661完成当命中TypeNS类型命名空间模块即归属于此时创建带code E0365的诊断结构体并调用err.emit()输出。E0364 与 E0365 如何分工看到上面第 4 步你可能已经留意到同一段 re-export 可见性检查逻辑还同时负责产生相邻错误码E0364。两者的分工在 imports.rs#L1684-L1696 中体现命中TypeNS典型的如模块、非泛型类型别名场景→ 创建CannotBeReexported*NS变体code E0365即本文主角命中其他命名空间值/宏等→ 创建CannotBeReexportedPrivate/CannotBeReexportedCratePubliccode E0364。两份官方文档相互印证E0364.mdPrivateitemscannot be publicly re-exported针对类型/值等一般条目其示例是pub use super::foo重导出私有函数fn foo()E0365.mdPrivatemodulescannot be publicly re-exported针对模块。因此调试时若报 E0364去检查被重导出的函数、常量、类型等条目自身的可见性若报 E0365则优先检查被重导出的模块是否漏写了pub。补充私有extern crate被pub use的关联处理E0365 家族还有一个特殊场景通过extern crate引入的 crate 本身是私有的却被pub use重导出。在 imports.rs#L1673-L1683 中rustc 先通过pub_use_of_private_extern_crate_hack识别该情况构造PrivateExternCrateReexport诊断同样携带code E0365定义于 diagnostics/mod.rs#L833-L843其建议为在被引用的extern crate前补pubconsider making the extern crate item publicly accessible不过这条路径走的是**提前 lintbuffered early lint**通道lint 名pub_use_of_private_extern_crate见 imports.rs#L1678-L1683与该错误码常规的err.emit()直发路径不同属于历史遗留兼容处理。为什么 Rust 要禁止私有模块被公开重导出这条规则本质上是Rust 可见性边界privacy boundary的必然推论模块系统的意义之一就是封装非pub模块内部的条目细节对下游不可见pub use的语义是把已可见的路径用另一个名字重新暴露给可能更广的使用者。如果允许把私有模块直接提升到对外可见就相当于绕过pub的显式声明偷偷扩大 API 面使库的公开接口取决于内部实现细节是否被顺手重导出破坏最小公开接口minimum public surface的可控性rustc 在解析阶段用可见性偏序比较vis.greater_than强制执行该边界让先声明pub再重导出成为唯一合规路径。从 API 设计角度这也是一条实用建议库 crate 对外暴露任何路径前都应检查链路上每个中间模块与条目的可见性是否都至少等于该路径的公开程度避免在重构模块树时被 E0364/E0365 这类可见性传递中断问题反复打断。深入阅读官方错误码文档本体compiler/rustc_error_codes/src/error_codes/E0365.md 与相邻错误 E0364.md错误码在rustc_error_codes中的注册表项compiler/rustc_error_codes/src/lib.rs#L196诊断消息结构体两种 E0365 文案、修复 note 与代码定位标注compiler/rustc_resolve/src/diagnostics/mod.rs#L796-L843触发逻辑命名空间分工、可见性偏序比较、early lint 分支compiler/rustc_resolve/src/imports.rs#L1612-L1709编译测试与期望输出用于验证本文所有示例的编译器实际行为tests/ui/error-codes/E0365.rs、tests/ui/error-codes/E0365.stderr若你在本地构建过本仓库的 rustc可直接用rustc --explain E0365查看内置的该错误说明其内容与上述官方文档一致。【免费下载链接】rustEmpowering everyone to build reliable and efficient software.项目地址: https://gitcode.com/GitHub_Trending/ru/rust创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考