前言#
昨天我们学完了第六章相关知识点match
今天我们开启第七章之旅
packages、crates and modules#
现在我们在小打小闹,最多就一俩个fn和struct。
而一个最小的项目都可能有好几十个fn或者其他,并且随着后续开发,体量会继续增大。
这个时候如果我们的代码都堆在一起就会非常恶心和难管理。
这就需要用到今天学的知识了,大概是以下这五点。
- Packages: A Cargo feature that lets you build, test, and share crates
- Crates: A tree of modules that produces a library or executable
- Modules and use: Let you control the organization, scope, and privacy of paths
- Paths: A way of naming an item, such as a struct, function, or module
packages和crates#
crate有箱的意思,但是中文说的怪怪的。。。。所以这里都叫crate了。
crates我们之前的文章里也有简单的说过,这里再加深下印象。
crate是rust编译器一次处理的最小的单位。我们知道rust的编译器是rustc,而我们操作rustc编译的时候只能是传入文件名,再往下就不能了。所以得出结论,一个文件既是一个crate。
而在这个文件中可能包含着很多的module模块,所以一个crate就是一个·module tree,包含多个module。
crate有两种类型:
library crate,也就是一个库crate,默认入口是lib.rs文件, 他们不能被编译成可执行文件,也没有main函数。与之相反,他们会被当做功能性的库,可被其他项目引入,比如我们之前用过的Rand的库,大多数时候,当说一个crate的时候都是指的库crate。binary crate,二进制crate,他们编译后会变成可执行的文件,也就是.exe文件(window系统)。有个点需要注意,那就是默认是main.rs文件的main函数作为入口,不需要配置。
而包packages相信大家都知道是啥了,这里简单说一下。
多个crate组合到一起,但不只是这样。它包含一个Cargo.toml的配置文件,用于配置项目。
有个点需要注意,那就是一个packages并不等价于一个project。它可以包含多个project,这些project共用同一个Cargo.toml文件。
一个packages至少拥有一个crate,可以包含多个binary crate, 但是只允许有一个library crate。
modules#
模块这玩意儿大家应该都很熟了,不过前端的开发应该对模块声明没有什么概念,因为工程化之后一个文件就是一个模块,如果你看过打包后的文件,其实一个立即执行函数,这就是一个模块。
扯远了,在rust中定义一个模块需要用到关键字mod,来看下例子
mod animal {
mod dog {}
mod cat {}
mod fish {}
} 我们声明了一个module:animal。然后又在里面声明了dog、cat、fish三个mod。
module是允许嵌套的,module的名字规范是小写字母 + 下划线。
一个crate中可以允许有多个mod,一个module里也允许有多个module。
module里的东西默认是私有的(private),如果想要访问的话,需要开启public.
不过在这之前,我们要知道怎么使用module里的东西,我们来改下我们的module,给它加个函数方便我们调用。
mod animal {
mod dog {
fn eat(food: &str) {
println!("{food}");
}
}
mod cat {}
mod fish {}
}
fn main() {
animal::dog::eat("狗粮");
}我们在dog这个module里声明了一个eat的method。
可以看到访问module里的东西需要用::符号。
然后由于module默认私有,所以这里自然就会报错

然后我们可以通过pub关键字让module变成public
mod animal {
pub mod dog {
pub fn eat(food: &str) {
println!("{food}");
}
}
mod cat {}
mod fish {}
}
fn main() {
animal::dog::eat("狗粮");
}这个时候就没报错了

在遇到这个animal的module时,编译器会去找这个module的位置,首先会在当前文件里找,找不到会去到src/animal.rs,找不到又会找src/animal/mod.rs文件。
而遇到dog这个module的时候则会这么找:
- 到当前文件里找
- 找
src/animal/dog.rs - 找
src/animal/dog/mod.rs
另外有个点我们需要注意,那就是module里的东西是不能访问外部的东西的

这个点和作用域里的不同,那么要怎么用module外的东西呢?下面会说到。
path访问module#
上面已经简单地说到了,访问一个module里的东西需要::符号,而且默认是私有的。
我们来加深下印象。
其实访问一个crate有两种方式:
- 绝对路径,需要用到关键词
crate,比如crate::animal::dog表示从根crate开始找,还记得我们上面说的吗?crate是由多个module组成,是module tree,所以你直接从crate开始找就表示从根root开始找。 - 相对路径,就是我们的例子写的那样
animal::dog::eat就是一个相对路径,表示当前文件中的animal这个module里的dog这个module里的eat方法。
所以上面那个找不到路径的报错我们可以使用绝对路径来访问

由于这个test方法是在文件最顶层的,所以用常规相对路径拿不到。
但如果我们还想用相对路径呢?官方提供了一个关键词super[4]
super`表示从当前作用域的上级作用域里找,但是我们这里有两层作用域,所以要用到两次`super
如果一个module能确定不会发生相对移动,那么用相对路径会是一个不错的选择。但是大多数情况下我们都是用绝对路径,因为这样会更安全。
现在我们都是在同一个文件中测试,但实际开发肯定是得跨文件的,所以我们来把module放到其他文件中。
拆分文件#
官方推荐了一种项目规范[6] ,一般创建项目的时候src下默认会有一个main.rs,这个前面说过了是binary crate的入口,这个时候我们还需要在src文件夹下创建一个lib.rs文件。为什么要这么做呢?我们把通用(非业务)的代码都放到这个lib.rs文件中,而前面说过这个lib.rs是作为library crate的入口,会被编译成一个库,这个库到时候就能被其他项目使用。
真是一举俩得~ 双赢。
话不多说,我们在src下创建个lib.rs文件,然后我们再创建一个animal的文件,用来存放animal这个module,不过我们不需要把animal这个名字也移动过去,因为crate默认是当前文件名字,如果再把animal移动过去则别的地方要这样写animal::animal。

ok这样就拆分完了,接下来我们要在别的文件中引用这个animal crate
使用use来引入模块#
我们可以通过use关键字,直接引入模块里的东西,这样即保证了绝对路径的安全,又不用写那么长了。
先来看下同文件中怎么使用。
mod animal {
pub mod dog {
pub fn eat(food: &str) {
println!("{food}");
}
}
mod cat {}
mod fish {}
}
use crate::animal::dog;
fn main() {
dog::eat("狗粮");
} use crate::animal::dog,这样就能直接用dog::eat而不用crate::animal::dog::eat这样了。
当然,这个mod必须是pub的。
而我们现在拆分成多个文件了,那么我们来试下从别的文件里引入。
main.rs文件中
mod animal;
use animal::dog;
fn main() {
dog::eat("狗粮");
}mod animal表示引入了animal这个module, 这样就能用这个animal.rs文件中的所有pub的mod了。
你也可以不用use关键字,直接animal::dog::eat也是可以的。
另外如果你不引入这个mod,你可以通过全局的路径引入,比如

concept是我们的package名字。
另外有一点,那就是一般我们想要放入lib中的crate都会抽一个专门的文件,比如这个animal,然后再引入lib.rs中,并且pub出去。比如:

as重命名#
有些module中的方法或者struct等的名字会重复,但由于module不同使得他们可以共存,这种时候我们就得确保上级路径也写了,但这样就未免会有些麻烦。
所幸rust提供了as关键字允许local name,就像js中的一样,可以重写名字。来看下例子

这个时候as就很有用了。
mod a {
pub fn create () {
println!("i am a");
}
}
mod b {
pub fn create () {
println!("i am b");
}
}
use a::create as a_create;
use b::create as b_create;
fn main () {
a_create();
b_create();
} 
这样就可以啦。
总结#
今天我们把整个第七章都学完了,学了package、crate、module、pub、use、as等,都是一些概念,比较好懂。
如果觉得对你有帮助的话,请务必点个赞,谢谢~
居家办公就是爽,今天摸了一天鱼~
参考#
- ^rust-packages-and-crates https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html#packages-and-crates
- ^rust-define-module-to-control-scoped-and-private https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html#defining-modules-to-control-scope-and-privacy
- ^rust-path-for-refer-to-an-item-in-module-tree https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#paths-for-referring-to-an-item-in-the-module-tree
- ^rust-use-super https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#starting-relative-paths-with-super
- ^rust-separating-modules-into-different-files https://doc.rust-lang.org/book/ch07-05-separating-modules-into-different-files.html#separating-modules-into-different-files
- ^rust-projet-recommand https://doc.rust-lang.org/book/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#best-practices-for-packages-with-a-binary-and-a-library
- ^rust-use https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html
编辑于 2022-12-10 10:23・IP 属地广东
