Package、Crate and Module
Rust 的代码组织
代码组织主要包括:
- 哪些细节可以暴露,哪些细节是私有的。
- 作用域内哪些名称有效。
这些组织被称为模块系统:
- Package(包):Cargo 的特性,使用者构建、测试、共享 crate 的基础。
- Crate(单元包):一个模块树,它可产生一个 library 或可执行文件。
- Module(模块)、use:让使用者控制代码的组织、作用域、私有路径。
- Path(路径):为 struct、function 或 module 等项命名的方式。
Package 和 Crate
- Crate 包括 binary 和 library 两种类型。
- Crate Root 则表示为源代码文件,Rust 编译器是从这里开始,组成你的 Crate 的 Module。
- 一个 Package :
- 包括一个 Cargo.toml,它描述了如何构建这些 Crates。
- 只能包含 0-1 个 library crate。
- 可以包含任意数量的 binary crate。
- 但必须至少包含一个 crate(library 或 binary)。
Cargo 的惯例
src/main.rs:
- 是 binary crate 的 crate root。
- crate 名与 package 名相同。
src/lib.rs:
- package 包含一个 library crate。
- library crate 的 crate root。
- crate 名与 package 名相同。
Cargo 把 crate root 文件交给 rustc 来构建 library 或 binary。
一个 Package 可以同时包含 src/main.rs 和 src/lib.rs。
- 一个 binary crate,一个 library crate。
- 这两个名称都与 package 名相同。
一个 Package 可以有多个 binary crate。
- 文件放在 src/bin。
- 每个文件都是单独的 binary crate。
定义 module 来控制作用域与私有性
Module 在一个 crate 内,将代码进行分组。增加代码可读性,易于服用。控制项目(item)的私有性,public、private。
建立 module 需要使用 mod 关键字,其可以嵌套。
//file: module_demo 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() {} } }
路径
- 为了在 Rust 的模块中找到某个条目,需要使用路径。
- 路径又两种形式:
- 绝对路径:从 crate root 开始,使用 crate 名或字面值 crate。
- 相对路径:从当前模块开始,使用 slef,super 或当前模块的标识符。
- 路径至少由一个标识符组成,标识符之间使用 ::。
私有边界 privacy boundary
- 模块不仅可以组织代码,还可以定义私有边界。
- 如果想要把函数或 struct 等设置为私有,可以将它放到某个模块中。
- Rust 中所有的条目(函数,方法,struct,enum,模块,常量)默认是私有的。
- 父级模块无法访问子模块中的私有条目。
- 子模块里可以使用所有祖先模块中的条目。
pub 关键字
- 使用 pub 关键字来将某些条目标记为公共的。
front_of_house
是同级别的,不标记 pub 也可以直接调用。
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}
pub fn eat_at_restaurant() {
crate::front_of_house::hosting::add_to_waitlist();
front_of_house::hosting::add_to_waitlist();
}
super 关键字
- super:用来访问父级模块路径中的内容,类似文件系统中的
..
。
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {
super::hosting::add_to_waitlist();
}
}
}
pub struct
- pub 放在 struct 前:
- struct 是公共的。
- struct 中默认的字段都是私有的。
- 在 struct 中的字段需要单独设置 pub 来变成公共的。
pub fn eat_at_restaurant() {
crate::front_of_house::hosting::add_to_waitlist();
front_of_house::hosting::add_to_waitlist();
let mut meal = back_of_house::Breakfast::summer("Rye");
meal.toast = String::from("Wheat");
// error : meal.seasonal_fruit = String::from("banana");
}
mod back_of_house {
pub struct Breakfast {
pub toast: String,
seasonal_fruit: String,
}
impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
}
pub enum
- 与 struct 类似将 pub 放在 enum 前即可声明为公共:
- enum 是公共的。
- enum 的变体也都是公共的。
mod demo {
pub enum Appetizer {
Soup,
Salad,
}
}
use 关键字
可以使用 use 关键字将路径导入到作用域内:
- 仍遵循私有性规则。
mod use_demo { pub mod hosting { pub fn add_waitlist() {} } } use crate::use_demo::hosting; pub fn eat_somgthing() { hosting::add_waitlist(); }
也可以用 use 来引用相对路径。
use 的习惯用法
- 函数:将函数的父级模块引入作用域(指定到父级),避免模糊函数是本地定义还是外部引用进入。
- struct、enum 等其他:指定完整路径(指定到本身)。
as 关键字
- as 关键字可以为引入的路径指定本地的别名。
use crate::use_demo::hosting as demoHosting;
pub fn eat_somgthing() {
demoHosting::add_waitlist();
}
使用 pub use 重新导出名称
- 使用 use 将路径(名称)导入到作用域后,该名称在此作用域内是私有的。
- 使用 pub 可以将 use 的暴露出来。
- 将条目引入到当前作用域,也可以将该条目导出被外部代码引入它们的作用域。
pub use crate::use_demo::hosting as demoHosting;
pub fn eat_somgthing() {
demoHosting::add_waitlist();
}
package
- Cargo.toml 添加依赖的包(package)。
- use 将特定条目引入到作用域。
- 标准库(std)也被当作外部包:
- 不需要修改 Cargo.toml 来包含 std。
- 需要使用 use 将 std 中的特定条目引入当前作用域。
使用嵌套路径清理大量的 use 语句
- 如果使用同一包或模块下的多个条目,可以使用嵌套路径在同一行内将上述条目进行引入:
- 路径相同的部分 ::{路径差异部分}。
use std::cmp::Ordering;
use std::io;
use std::{cmp::Ordering, io};
use std::io;
use std::io::Write;
use std::io::{self, Write};
通配符 *
- 可以使用
*
将路径中所有的公共条目都引用到作用域中。- 注意:谨慎使用。
- 应用场景:
- 测试:将所有被测试代码引入到 tests 模块中。
- 优势被用于预导入模块。
use std::collections::*;
如何将模块拆分为不同的文件
- Rust 可以将模块内容移动到其他文件,在模块定义时,如果模块名后边是
;
,而不是代码块:- Rust 会从与模块同名的文件中加载内容。
- 模块树的结构不会发生变化。
- 随着模块逐渐变大,该技术可以把模块的内容移动到其他文件中。