模块定义
mod
模块 让我们可以将一个 crate 中的代码进行分组,以提高可读性与重用性。模块还可以控制项的 私有性,即项是可以被外部代码使用的(public),还是作为一个内部实现的内容,不能被外部代码使用(private)。
模块定义
模块通过关键字 mod 加模块定义, 例如:
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
文件 src/main.rs 和 src/lib.rs, 对应的模块是 crate, 箱(crate)的模块结构(module structure), 也叫做模块树(module tree):
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
模块 crate 默认存在, 不需要通过关键字 mod 方式来定义。与文件系统类似, 文件系统的根节点是 /, 箱(crate)的根节点是 crate
.
- 绝对路径,从箱(crate)的根节点开始, 箱(crate)的名称, 或
crate
- 相对路径,从当前模块开始, 可以使用
self
,super
// 文件内绝对路径 Absolute path
crate::front_of_house::hosting::add_to_waitlist();
// 相对路径 Relative path
front_of_house::hosting::add_to_waitlist();
同文件模块
相关代码和数据被分组到一个模块中并存储在同一文件中。
fn main() {
greetings::hello();
}
mod greetings {
// ⭐️ By default, everything inside a module is private
pub fn hello() { // ⭐️ So function has to be public to access from outside
println!("Hello, world!");
}
}
模块也可以嵌套。
fn main() {
phrases::greetings::hello();
}
mod phrases {
pub mod greetings {
pub fn hello() {
println!("Hello, world!");
}
}
}
self 关键字用于引用相同的模块,而 super 关键字用于引用父模块。还可以使用 super 关键字从模块内部访问根函数。
fn main() {
greetings::hello();
}
fn hello() {
println!("Hello, world!");
}
mod greetings {
pub fn hello() {
super::hello();
}
}
编写测试时,最好在测试模块中编写测试,因为它们仅在运行测试时才编译。
fn greet() -> String {
"Hello, world!".to_string()
}
#[cfg(test)] // only compiles when running tests
mod tests {
use super::greet; // import root greet function
#[test]
fn test_greet() {
assert_eq!("Hello, world!", greet());
}
}
模块可见性
- 所有元素, 函数 functions, 方法 methods, 结构体 structs, 枚举 enum, 模块 modules, 常量 constants, 默认是私有的, 对外公开(public), 需要通过关键字
pub
声明- 即便是共有的结构体(public structs), 内部的元素(fields)和方法(methods)仍是私有的(private)
- 共有的枚举(public enums), 其所有变天(variants)也同为共有(public)
- 父模块中的元素, 不能使用子模块中的私有元素
- 子模块中的元素, 可以使用父模块的元素
譬如将制作一个名为 print_things 的 mod,它具有一些与打印相关的功能。
mod print_things {
use std::fmt::Display;
fn prints_one_thing<T: Display>(input: T) { // Print anything that implements Display
println!("{}", input)
}
}
fn main() {}
可以看到我们在 print_things 里面写了std::fmt::Display;
,因为它是一个单独的空间。如果您在 main()中使用std::fmt::Display;
编写,将无济于事。同样,我们现在不能从main()
调用它。如果在 fn 前面没有 pub 关键字,它将保持私有状态。让我们尝试在没有发布的情况下调用它。这是一种编写方式:
// 🚧
fn main() {
crate::print_things::prints_one_thing(6);
}
crate
意味着在该文件中。里面是 mod print_things,最后是 prints_one_thing() 函数。您可以每次编写该代码,也可以编写用于导入的用法。现在,我们可以看到错误提示它是私有的:
// ⚠️
mod print_things {
use std::fmt::Display;
fn prints_one_thing<T: Display>(input: T) {
println!("{}", input)
}
}
fn main() {
use crate::print_things::prints_one_thing;
prints_one_thing(6);
prints_one_thing("Trying to print a string...".to_string());
}
error[E0603]: function `prints_one_thing` is private
--> src\main.rs:10:30
|
10 | use crate::print_things::prints_one_thing;
| ^^^^^^^^^^^^^^^^ private function
|
note: the function `prints_one_thing` is defined here
--> src\main.rs:4:5
|
4 | fn prints_one_thing<T: Display>(input: T) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
很容易理解函数 print_one_thing 是私有的。它还显示了在哪里可以找到该函数。这是因为一旦开始使用 mod,您可能还会使用多个文件,并且很难找到内容。现在我们只写 pub fn 而不是 fn,一切正常。
mod print_things {
use std::fmt::Display;
pub fn prints_one_thing<T: Display>(input: T) {
println!("{}", input)
}
}
fn main() {
use crate::print_things::prints_one_thing;
prints_one_thing(6);
prints_one_thing("Trying to print a string...".to_string());
}
6
Trying to print a string...