在开始编写代码前,语法概念之类的其实不重要,理解项目的组织架构是首要的。在rust中,模块管理与包的概念与我熟悉的其它语言都有所不同,主要是有Crate与mod概念,中文翻译为箱和模块。
每一个通过cargo
创建的项目称之为一个Crate,无论是以库形式(lib.rs
)或者是二进制形式(main.rs
)。其中唯一的rs文件称之为Crate root,也叫作箱根,也是一个Crate的入口点,子模块的加载之类的,都在对应的箱根中有声明。
系统的标准库也有一个箱根,名为std
,我们可以直接引用该Crate而不必在Cargo.toml
的依赖中添加,因为安装好rust
之后标准库也安装好了,不必从第三方进行下载。
我们自己创建的Crate,假设我们自己别的项目需要加载,则需要在该项目的Cargo.toml
的[dependencies]
节中添加对应的Crate,比如如下的实例:
[package]
name = "helloworld"
version = "0.1.0"
authors = ["sryan"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
sep_restaurant = { path = "../sep_restaurant" }
在上述的实例中,我们引用了一个本地的名为sep_restaurant
的Crate,而该包的路径是相对于Cargo.toml
的。
对于外部的箱根,内部的Crate也就成了创建的项目名了,也就是从Crate变成了helloworld了,当然内部可以直接用Crate作包内的绝对路径引用,这个会在use
这段来进行介绍。
rust
的模块和go
的模块有点儿差异,go
是以文件夹作为模块的,并且模块内可以自由引用,rust
则可以在单个文件中创建多个模块。
所有模块的根都在箱根中,也就是lib.rs
或者是main.rs
,所有模块的加载都是在这两个箱根中的,也就是我们在lib.rs
中假设定义了一个hello
函数:
mod first_level_mod {
pub fn hello() {
println!("hello");
}
}
则该模块是属于箱根的第一层模块:
Crate --- first_level_mod
假设我们继续在first_level_mod
中继续新增一个模块:
mod first_level_mod {
pub fn hello() {
println!("hello");
}
mod second_level_mod {
pub fn world() {
println!("world")
}
}
}
则second_level_mod
就是第二层的模块了,对应的父子关系也就变成了:
Crate --- first_level_mod
|
--- second_level_mod
对于同级的模块,第一层是相互可见的:
mod first_level_mod {
pub fn hello() {
println!("hello");
}
mod second_level_mod {
pub fn world() {
println!("world")
}
}
}
pub fn see_mod() {
first_level_mod::hello();
}
第一层是可见的,我们可以直接访问,但是其中的模块,必须依赖它的可见性标识pub
来决定,默认是不可见的。在上述例子中,我们的see_mod
函数和first_level_mod
都挂在Crate
上属于同一级,所以是互相可见的。
上面的例子主要是将所有的代码放到一个文件中,假设我们需要多个文件来拆分项目,我们该怎么操作呢?
我们可以通过在文件中声明一个模块,但是不去实现,类似于我们在lib.rs
中下如下的语句:
mod another_mod;
这个时候加载了箱根后,会去加载对应的模块,这个时候有两种加载策略:
another_mod.rs
文件,其中不必声明模块,所有的代码都在another_mod
的模块之中another_mod
目录下的mod.rs
文件这样我们就完成了模块与文件分离的操作。我们这里还是首先得明白rust
的加载方式,先加载箱根,然后根据箱根内的模块定义与引用来加载不同的模块,所以必定所有的模块都是从箱根这里一级一级的导入的。
假设我们创建了一个another_mod.rs
的文件,在其中又定义了一个third_mod.rs
,则这里的模块父子关系就是:
Crate --- another_mod --- third_mod
可以看出一层一层的加载模块。在这里我们可以总结出以下模块的加载规律。
当扫描到了模块定义之后(比如为mymod
),则根据当前的所属模块,比如当前属于箱根,则直接在箱根同级目录中寻找同名文件mymod.rs
或者是同名目录中的mod.rs
;若当前属于某一个模块mod_xx
,则会寻找mox_xxx/mod.rs。
重点在于除了在箱根定义的模块可以直接在箱根中定义同名文件,其余的都要放入模块对应的全路径中。
说到了模块的多文件,我们假设要引用模块的话,该如何引用呢?
最简单的我们假设要引用模块,比如一个标准库,我们可以简单的使用:
let mut hmap: std::collections::HashMap<i32, i32> = std::collections::HashMap::new();
可以看出引用的路径非常的冗长,我们能不能简化一下呢?我们可以通过use
关键字来简化引用的路径,比如上述的HashMap
我们可以通过use
简写成如下的形式:
use std::collections::HashMap;
let mut smap: HashMap<i32, i32> = HashMap::new();
通过use
关键字,我们简化了整个HashMap
的定义。
假设我们要引入第三方库,则需要更改Cargo.toml
了,这个在上述已经提到过了,这里就不再多说了。