crate是Rust的最小代码单位,有二进制项和库两种形式,二进制项有main函数,库没有main函数。包(package)是提供一系列功能的一个或多个crate,一个包会包含一个Cargo.toml文件,包可以包含一个库crate(library crate)或任意多个二进制crate(binary crate),但至少需要包含一个crate。
用Cargo创建一个新的包,里面会有src文件夹和Cargo.toml文件,src文件夹里面有一个main.rs文件,这是与包同名的二进制crate根,如果有lib.rs文件,则是与包同名的库crate根。
💡 提示
将文件放在src/bin文件夹下,Cargo会自动将其视为一个独立的二进制crate,并生成一个对应的二进制crate的根。
模块应用🔗
模块化是一种非常重要的编程范式,它可以帮助开发者编写更清晰、更易于维护、更易于扩展的代码。
模块声明🔗
编译器从crate根文件(src/main.rs或src/lib.rs)开始寻找需要被编译的代码,如果你在crate根文件中声明一个新模块,例如使用mod my_module;,编译器会在crate根目录下寻找src/my_module.rs文件或src/my_module/mod.rs文件。
子模块声明🔗
如果在src/my_module.rs定义了mod my_submodule;,编译器会在src/my_module目录下寻找my_submodule.rs文件或my_submodule/mod.rs文件。
私有和公有🔗
模块的代码默认对其父模块私有,如果想让模块中的项对父模块外部可见,可以使用pub关键字。
ℹ️ 注意
将结构体定义为pub,则结构体的字段默认为私有,如果想让字段公有,需要使用pub关键字。
若结构体某个字段为私有,则需要提供一个关联函数来构建实例,否则外部无法创建实例。
use关键字🔗
use crate::my_module::my_submodule;,这会导入my_submodule模块,之后就可以直接使用my_submodule,而不需要my_module::my_submodule。
模块树🔗
考虑src/lib.rs中的这样一段代码:
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() {}
}
}
这段代码中模块树结构如下:
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
使用模块管理代码能让代码结构更清晰,更易于维护。
引用模块🔗
要调用模块中的函数,需要用到::操作符,关于路径有绝对路径(absolute path)和相对路径(relative path)两种,绝对路径从crate开始,相对路径从当前模块开始,使用self关键字表示当前模块,super关键字表示父模块。
💡 提示
使用use crate::front_of_house::hosting;,这会导入hosting模块,之后就可以直接使用hosting,而不需要front_of_house::hosting。use语句只适用于其所在作用域。
ℹ️ 注意
一般来讲,一个包同时包含二进制crate和库crate,此时二进制crate中只保留可执行文件的代码,被调用的代码都放在库crate中,模块树也应该定义在src/lib.rs中。
使用pub use关键字可以将模块导入当前作用域后,其他模块也可以倒入,例如:
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
若不使用pub use,外部代码需要使用路径
restaurant::front_of_house::hosting::add_to_waitlist()来调用add_to_waitlist函数。现在,外部代码可以使用restaurant::hosting::add_to_waitlist()来调用add_to_waitlist函数。