Managing Growing Projects with Packages, Crates, and Modules

Packages and Crates

Package

Package(包) : Cargo 的特性,让你构建、测试、共享 crate

Crate(单元包) : 模块树,它可产生一个 library 或可执行文件

Module(模块), use : 让你控制代码的组织、作用域、私有路径

Path(路径) : 为 struct、function 或 module 等项命名的方式

Cargo 的惯例

1
2
3
4
5
6
7
8
src/main.rs:
- binary cratecrate root
- crate 名与 package 名相同

src/lib.rs:
- package 包含一个 library crate
- library cratecrate root
- crate 名与 package 名相同
1
2
3
4
5
6
7
8
一个 package 可以同时包含 src/main.rs 和 src/lib.rs
- 名称与 package 名相同

一个 package 可以有多个 binary crate
- 文件放在 src/bin
- 每个文件是单独的 binary crate

一个 package 最多有一个个 library crate

Defining Modules to Control Scope and Privacy

module

1
2
3
4
Module:
- 在一个 crate 内,将代码进行分组
- 增加可读性,易于复用
- 控制项目(item)的私有性。public、private
1
2
3
建立 module:
- 使用 mod 关键字
- 可以嵌套

例如

1
2
3
4
5
mod front_of_house {
mod hosting {
fn add_to_waiting() {}
}
}

pub 关键字

1
2
3
pub struct:
- struct 是公共的
- struct 的字段默认私有

例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mod front_of_house {
pub struct Breakfast {
pub toast: String,// public
seasonal_fruit: String,// private
}

impl Breakfast {
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
}

fn eat_at_restaurant() {
let mut meal = front_of_house::Breakfast::summer("Rye");
meal.toast = String::from("Wheat");
meal.seasonal_fruit = String::from("blue");// Error! private field
}

Paths for Referring to an Item in the Module Tree

Path

绝对路径:从 crate root 开始,使用 crate 名 或 字面值 crate

相对路径:从 当前模块开始,使用 self,super 或当前模块的标识符

1
2
3
4
5
6
7
8
9
10
11
12
mod front_of_house {
mod hosting {
fn add_to_waiting() {}
}
}

fn eat_at_restaurant() {
// 绝对路径
crate::front_of_house::hosting::add_to_waiting();
//相对路径
front_of_house::hosting::add_to_waiting();
}

Cargo run 会发现错误

1
2
3
4
5
6
/*
house::hosting::add_to_waiting();
| ^^^^^^^ -------------- function `add_to_waiting` is not publicly re-exported
| |
| private module
*/

这就是因为默认私有,使用 pub 关键字将条目标记为公共的

1
2
3
4
5
mod front_of_house {
pub mod hosting {
pub fn add_to_waiting() {}
}
}

父级模块无法访问子模块中的私有条目

子模块可以访问所有祖先模块中的条目

super 关键字,表示上一级

1
2
3
4
5
6
7
8
9
10
11
mod front_of_house {
pub mod hosting {
pub fn add_to_waiting() {
super::get();// 调用上一级的 get
}
}

fn get() -> String {
return String::from("2024");
}
}

Bringing Paths into Scope with the use Keyword

遵守私有性原则

一般将函数的的父级引入模块

struct,enum,其他:指定完整路径(指定到本身)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mod front_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"),
}
}
}
}

use crate::front_of_house::Breakfast;// 绝对路径

fn eat_at_restaurant() {
Breakfast::summer("abc");//这时候就可直接用
}

同名条目:指定到父级

1
2
3
4
5
use std::fmt;
use std::io;

fmt::Result;
io::Result;

as 关键字

1
2
use std::fmt::Result as FmtResult;
use std::io::Result as IoResult;

这时候就可以直接用 as 后的东西代替前面的东西

use 引入默认私有,外部代码看不到

1
2
3
4
5
fn eat_at_restaurant() {
use crate::front_of_house::Breakfast; // 外部看不到
let x = Breakfast::summer("abc");
println!("{:#?}", x);
}
1
2
3
4
5
fn eat_at_restaurant() {
pub use crate::front_of_house::Breakfast;// 外部看得到
let x = Breakfast::summer("abc");
println!("{:#?}", x);
}

使用外部包 package

Cargo.toml 下添加 package

package 下载 https://crates.io/

1
2
3
4
// Cargo.toml 下
[dependencies]
rand = "0.7.3"
// package

需要 cargo build, 下载慢可以换国内镜像源

使用 use 将特定条目引入作用域

1
use rand::Rng;

std 默认内置(即不需要在 dependencies 放 package),但需要用 use 来引入作用域

例如引入 HashMap

1
use std::collections::HashMap;

使用嵌套路径清理大量 use 语句

1
2
3
use std::collections::HashMap;
use std::collections::HashSet;
use std::collections::BTreeMap;

我们发现前面都是一样的

使用语法 相同部分::{不同部分,...}

1
use std::collections::{HashMap, HashSet, BTreeMap};

当我们需要本身和它下面的条目时,使用 self

例如:引入 std::collections 和 std::collections::HashMap

1
use std::collections::{self, HashMap};// self

当我们需要一个模块下所有条目时,我们可以使用通配符 * 来引入

1
use std::collections::*;

一般测试时才会用

Separating Modules into Different Files

将模块内容移动到其他文件

模块定义时,如果模块名后面是;,而不是代码块{code},rust 会从与模块同名的文件中加载内容,模块树结构不会变化

1
2
3
4
5
6
7
// lib.rs 内
mod hosting;

// hosting.rs 内
mod hosting {
// code
}

如果是嵌套模块,需要放到对应文件夹下