mod是编译单元,不只是目录
Rust 用 mod 把代码挂到模块树上。文件名通常只是模块的默认来源:runtime_module/mod.rs 声明子模块,子文件中的内容才会进入同一棵模块树。
Rspack 的 crates/rspack_plugin_runtime/src/runtime_module/mod.rs 会重新导出多个运行时模块;module_chunk_loading.rs 再通过 use super::utils::get_output_dir 访问同级父模块下的工具。这种组织让每个运行时模块保持独立,同时由 mod.rs 提供统一出口。
最小例子:
mod config {
pub const NAME: &str = "rspack";
pub mod output {
pub fn describe() -> String {
format!("{} output", super::super::NAME)
}
}
}
fn main() {
println!("{}", config::output::describe());
}
这里 config 是根模块的子模块,output 是 config 的子模块。super::super::NAME 先回到 config,再回到根模块;NAME 必须是 pub,否则 output 不能读取它。
pub控制边界,默认是私有
模块项默认只对当前模块及其子模块可见。pub 不是“整个工程都能访问”的开关,而是把该项暴露给父模块之外的路径;跨 crate 使用时还需要 crate 本身在依赖清单中声明,并且路径上的每一级模块都可见。
Rspack 的 ModuleChunkLoadingPlugin 声明为 pub struct,并在 lib.rs 中通过 pub use module_chunk_loading::ModuleChunkLoadingPlugin; 重新导出。这样其他 crate 不必依赖内部文件路径,可以从 rspack_plugin_runtime::ModuleChunkLoadingPlugin 访问它。这个 re-export 是 API 边界:内部文件可以调整,公开入口保持稳定。
下面的错误示例会被编译器拒绝:
mod internal {
fn secret() -> &'static str {
"private"
}
}
fn main() {
println!("{}", internal::secret());
}
错误原因不是函数没有实现,而是 secret 对根模块外部路径不可见。将它改为 pub fn secret() 后,路径才合法。可见性错误通常意味着 API 边界没有设计好,而不是应该盲目给所有项目加 pub。
use crate与use super的路径语义
Rspack 运行时模块中有两类典型导入:
use super::utils::get_output_dir;
use crate::{LinkPrefetchData, LinkPreloadData, RuntimePlugin};
crate::从当前 crate 的根模块开始解析。它适合引用跨多个子模块共享的类型、trait 或公共工具。super::从当前模块的父模块开始解析。它适合同一模块树内部的邻近实现,表达“我依赖父模块下的兄弟项”。
这两个前缀都是绝对的模块树路径,不依赖调用者从哪里进入当前函数。相比大量 ../../ 文件路径,它们能直接表达 Rust 的命名空间关系。
如果模块移动了位置,super:: 可能需要调整;如果希望内部实现移动而调用方不变,可以在上层 mod.rs 用 pub use 建立稳定入口,再让外部代码使用 crate::runtime_module::ModuleChunkLoadingRuntimeModule 这样的路径。
- 最小可运行示例与验证
下面的示例同时使用 crate:: 和 super:::
mod model {
pub struct Chunk {
pub id: u32,
}
}
mod runtime {
use crate::model::Chunk;
pub mod formatter {
use super::Chunk;
pub fn render(chunk: &Chunk) -> String {
format!("chunk:{}", chunk.id)
}
}
}
fn main() {
let chunk = model::Chunk { id: 7 };
println!("{}", runtime::formatter::render(&chunk));
}
我用 rustc 编译并运行该示例,输出为:
chunk:7
runtime 先用 crate::model::Chunk 把类型导入自己的作用域;嵌套的 formatter 再用 super::Chunk 继承父模块已经导入的名称。注意,use 只创建当前模块的名称别名,不会改变类型的所有权,也不会把名称自动导出给子模块;需要对外暴露时仍要写 pub use。
- 与 Rspack 工程的对应关系
读取 ModuleChunkLoadingPlugin 时,可以按以下顺序定位:
lib.rs通过pub use暴露插件,并在 Import 分支构造它。module_chunk_loading.rs通过use rspack_core::{...}引入跨 crate API。- 同一 crate 的运行时模块通过
use crate::runtime_module::{...}访问公共模块出口。 - 运行时模块文件通过
use super::utils::...使用父模块树中的工具。
这个路径层次把公开 API、crate 内共享能力和相邻实现区分开。读源码时先看模块声明与 re-export,再追具体函数,通常比从文件名猜调用关系更可靠。
- 常见误区与适用边界
- 文件夹存在不代表模块自动存在;必须由
mod声明把它纳入模块树。 use是导入名称,不是可见性提升;私有项被导入后仍然私有。pub只解决 Rust 模块可见性,不会替代 Cargo 的 crate 依赖声明。- 内部实现优先保持私有,通过
pub use暴露少量稳定入口,能降低重构成本。
适合正在读 Rust 大型项目源码的开发者,用 Rspack 实例把模块边界与路径解析讲得具体,能帮助建立从入口到实现的可靠阅读路径。