7.1 Package, Crate, Module
概览
Package
- Cargo的特性,用于构建、共享和测试crate
Crate
- 一个模块树,可以产生一个library或可执行文件
Module
- use,让我们控制代码的组织、作用域和私有路径
Path
- 为struct,module,function等命名的方式
Package和crate
Crate分为binary和library两种
- 源代码文件被称作crate root,组成crate的root module,也是编译器开始的地方
在一个package里面
- 只能包含一个
Cargo.toml
,其描述了如何构建这些crate - 只能包含0-1个library crate,但binary crate不限
- 不过必须至少包含1个crate
上面的有些抽象,以下是一个具体的粒子
在执行cargo new my_project
后,cargo会创建一个package(项目)
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文件
对于更多的binary crate,需要放在src/bin
文件夹下面,每个源文件是一个单独的binary crate
Crate的作用是将相关功能组合到一个作用域内,便于在项目之间进行共享(防止冲突)
- 有点像C++里面header + namespace的做法?
例如,之前使用过的rand crate,在访问时需要使用rand
Module
在一个crate里面,我们可以将代码进行分组,即不同的module
Module可以控制内部条目的私有性,例如public和private
为了建立一个module,
- 使用
mod
关键字 - 可嵌套
- 可包含其它项(如struct,enum,常量,trait,函数等)的定义
例如,在lib.rs
中:
1 | mod front_of_house { |
7.2-3 Path
为了调用这些定义的模块,我们需要借助它们的path,例如
1 | mod front_of_house { |
- 绝对路径从crate的根目录出发,以
crate
作为出发点,来寻找对应模块里的功能 - 相对路径从调用者所在的目录出发,来寻找功能
- 一般建议使用绝对路径,防止代码块移动造成问题
事实上,上面的代码是有误的,因为
模块
front_of_house
是私有的(对外部调用)此外
hosting
模块也是私有的
私有边界(private boundary)
Rust的模块中的所有条目都默认是私有的(struct,enum,常量,trait,函数,子模块等)
声明公共的关键字是pub
,因此需要改成
1 | mod front_of_house { |
虽然父模块无法访问子模块的私有条目,但是子模块可以访问所有祖先模块的条目
同级条目可以互相调用
Super关键字
为了访问父模块(上级path),使用super
关键字,类似bash中的..
表示上级目录
例如
1 | mod front_of_house { |
- 其中
super::serve_order();
也可以替换成绝对路径crate::serve_order();
类似的,模块中定义的struct的条目也是默认私有,例如
1 | mod back_of_house { |
与struct不同,如果pub
声明在枚举类型enum前面,则enum的所有变体都是公共的!
例如
1 | mod back_of_house { |
- 和struct不同,enum里的变体如果是私有的,那么就没有意义了
- 因此,enum是特例
7.4 use
关键字
我们可以使用use
关键字将路径导入到当前作用域内
- 仍然遵守私有性原则
例如
1 | mod front_of_house { |
- 由于私有性原则依然存在,所以调用私有函数
some_function()
会报错
除了绝对路径,use
还可以指定相对路径,例如上面的例子里,就是
1 | use front_of_house::hosting; |
事实上,如果调用add_to_waitlist()
,我们可以直接
1 | use crate::front_of_house::hosting::add_to_waitlist(); |
但这样在使用函数的时候,难以区分到底是引入的还是本地定义的,因此建议引用到至少一层上级目录
同名条目的as
关键字
有可能在两个不同的模块中引入同名的条目,这个时候除了使用上级目录区分,还可以使用as
关键字,类似Python中的用法,例如
1 | use std::fmt::Result; |
7.5 use
关键字 续
使用use
关键字将路径或名称导入到作用域之后,该名称在此作用域也是默认私有的
例如
1 | mod front_of_house { |
- 虽然上面的作用域引入了
hosting
,但外部代码是不能使用use
获得的部分的
如果希望外部作用域也能使用到,可以借助
1 | pub use crate::front_of_house::hosting; |
使用外部package
在Cargo.toml添加依赖的package
- 随后cargo会从 https://crate.io 下载对应的package
使用use
将特定条目引入作用域,例如
1 | use rand::Rng; |
在Rust中,标准库std
是默认引入的,即不需要在Cargo.toml
引入,例如,我们可以直接调用
1 | use std::collections::HashMap; |
嵌套引入
对于下面的情况
1 | use std::io; |
可以简写成
1 | use std::{cmp::Ordering, io}; |
对于下面的情况
1 | use std::io; |
我们可以简写成
1 | use std::io::{self, Write}; |
通配符
通配符*
可以把路径中的所有公共条目都导入到作用域,例如
1 | use::std::collections::*; |
- 谨慎使用通配符!尽量不要使用
7.6 拆分模块到不同文件
例如在lib.rs
文件中:
1 | mod front_of_house { |
可以修改为
1 | mod front_of_house; |
随后可以在另一个例如front_of_house.rs
文件中定义
1 | pub mod hosting { |
这并不会影响lib.rs
中的use
等定义,因为Cargo会自动去寻找相关的模块并绑定到mod front_of_house
的位置
如果进一步要将hosting
这个子模块单独拿出来定义,需要将源文件放在src/front_of_house/
文件夹下才可以
- 与代码组织相同的路径方式