1. 模块简介
什么是模块?
在 Rust 中,模块是一种组织代码的方式。通过模块,你可以将相关的函数、结构体、枚举、常量等分组在一起,并控制它们的可见性(即哪些部分是公共的,哪些是私有的)。这有助于保持代码清晰、可维护,并避免命名冲突。
Rust 模块系统的优势
- 封装性:允许你隐藏实现细节,只暴露必要的接口。
- 代码组织:帮助管理大型代码库,使得代码更容易理解和维护。
- 命名空间管理:防止名称冲突,尤其是在大型项目中。
2. 声明模块
使用 mod
关键字
要声明一个模块,你需要使用 mod
关键字。例如,要在 main.rs
中定义一个名为 greetings
的模块:
rust
// src/main.rs
mod greetings;
fn main() {
println!("Hello from the main function!");
}
然后,在同一目录下创建一个名为 greetings.rs
的文件来定义该模块的内容:
rust
// src/greetings.rs
pub fn hello() {
println!("Hello from the greetings module!");
}
文件与目录映射到模块
除了上述简单的模块声明外,Rust 还支持更复杂的层次结构。例如,如果你想定义一个子模块,可以在 src
目录下创建一个同名文件夹并在其中添加 mod.rs
文件或直接根据文件路径声明子模块。
例如,创建一个 network
模块和它的子模块 http
:
src
├── main.rs
└── network
|__ mod.rs
└── http.rs
然后在 main.rs
中声明:
rust
// src/main.rs
mod network;
fn main() {
network::http::get();
}
而在 src/network/http.rs
及src/network/mod.rs
中定义:
rust
// src/network/http.rs
pub fn get() {
println!("Performing an HTTP GET request.");
}
// src/network/mod.rs
pub mod http;
3. 访问控制
pub
关键字的作用
默认情况下,模块中的所有项都是私有的。这意味着它们只能在定义它们的模块内部访问。若要使某项可以从外部访问,需要使用 pub
关键字将其标记为公共。
例如,在 greetings.rs
中:
rust
// src/greetings.rs
pub fn hello() {
println!("Hello from the greetings module!");
}
fn goodbye() {
println!("Goodbye from the greetings module!");
}
这里,hello
函数是公共的,而 goodbye
是私有的。因此,你只能在 greetings
模块外部调用 hello
函数。
模块私有性的重要性
模块的私有性有助于封装和隐藏实现细节,仅暴露必要的接口给外部使用者。这是软件设计中的一个重要原则,它增强了代码的安全性和可维护性。
4. 嵌套模块
如何在模块中定义子模块
除了通过文件系统来组织模块之外,你还可以在模块内部直接定义子模块。例如,在 greetings.rs
中定义一个子模块 farewells
:
rust
// src/greetings.rs
pub mod farewells;
fn hello() {
println!("Hello from the greetings module!");
}
然后在 greetings/farewells.rs
中定义内容:
src
├── main.rs
└── greetings
├── mod.rs // 或者 greetings.rs
└── farewells.rs
rust
// src/greetings/farewells.rs
pub fn goodbye() {
println!("Goodbye from the farewells module!");
}
使用路径访问嵌套模块中的项
现在,你可以在 main.rs
中这样访问 farewells
模块中的 goodbye
函数:
rust
// src/main.rs
mod greetings;
fn main() {
greetings::farewells::goodbye();
}
5.实战案例
假设有如下的项目目录结构:
html
📦src
┣ 📂mod1
┃ ┗ 📜test1.rs
┗ 📜main.rs
其中,test1.rs
的内容为:
rust
// test1.rs
pub fn run() {
println!("Hello, I'am from mod1::test1!");
}
如果我们需要在main.rs
中访问 test1.rs
中的 run()
方法,该如何操作?
我们知道,rust中,mod
关键字用于声明模块,可以放在 main.rs
文件中,也可以放在专门的文件中(mod.rs
)。
只声明mod1
,可以找到子模块吗?
rust
/*
unresolved module, can't find module file: mod1.rs, or mod1/mod.rs rust-analyzerE0583
file not found for module `mod1`
to create the module `mod1`, create file "src\mod1.rs" or "src\mod1\mod.rs"
if there is a `mod mod1` elsewhere in the crate already, import it with `use crate::...` insteadrustc
*/
// error[E0583]: file not found for module `mod1`
mod mod1;
fn main() {
println!("Hello, world!");
mod1::test1::run();
}
从编译器的提示可知,当我们在src下的main.rs 中声明一个mod1
时, 默认是从同目录的mod1.rs, or mod1/mod.rs
文件开始进行模块查找的。
显然找不到。
解决方案1
在 mod1
文件夹下创建mod.rs
, 在其中声明子模块。
rust
// mod1/mod.rs
// 注意 `pub` 关键字
pub mod test1;
解决方案2
使用模块嵌套声明。对于简单的测试,如果我们不想单独创建一个 mod.rs
文件,我们也可以在main.rs
中使用模块嵌套声明,如下:
rust
mod mod1 {
// 注意 `pub` 关键字
pub mod test1;
}
fn main() {
println!("Hello, world!");
// 注意,不能直接通过 test1::run() 访问,必须使用全路径
mod1::test1::run();
}
6. 总结
1)模块声明与定义的区别:
在 Rust 中,mod 关键字用于声明一个模块。当你在某个文件中使用
mod some_module
; 时,Rust 会尝试在同目录下查找名为some_module.rs
的文件或some_module/mod.rs
(对于子目录模块)作为该模块的内容。
2)文件系统映射模块结构
Rust 强制要求模块的组织必须遵循一定的文件和目录结构。例如,如果你想有一个
mod1::test1
模块结构,你需要在 src 目录下有mod1/test1.rs
或者mod1/mod.rs
加mod1/test1.rs
文件。这是因为 Rust 将这些文件视为模块声明的物理表示形式。
3) 可以在源文件内部直接定义模块的内容
pub mod {}
语法允许你在源文件内部直接定义模块的内容,而不是通过单独的文件或目录结构来组织模块。这对于原型开发测试或当你有一个很小的模块,并且不值得为它创建一个新的文件时非常有用。
如:
rust
// src/main.rs
fn main() {
let resut = utils::add(1, 2);
println!("resut: {}", resut);
}
// 定义一个名为 utils 的公共模块
mod utils {
// 在此定义 utils 模块的内容
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
}