在谈Rust中项目模块的组织方式之前,先聊一下三个概念
- Package
- Create
- Module
这里我就不把上面三个单词翻译成中文了,感觉没有必要,我们以实操来解释一下这三个概念就好
Package
Package
其实就是一个项目,是 cargo
提供让我们创建,测试和分享的
二进制Package
当我们输入 cargo new hello
会发现提示:Created binary (application) hello
package。
我们通过cargo
创建的hello
,其实就是一个package
go
$ cargo new hello
Created binary (application) `hello` package
同时在根目录创建了Cargo.toml
文件,该文件里面并没有提到 src/main.rs
作为程序的入口,原因是 Cargo 有一个惯例:src/main.rs
是二进制包的根文件 ,该文件编译之后的名字和我们创建的Package
名字相同,在这里都是hello
,所有代码都是从该文件的main函数开始执行
ini
[package]
name = "hello"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
我们使用cargo run
看一下编译之后的结果
arduino
$ cargo run
hello world
我们可以看到有一个名为hello
的二进制文件,我们直接运行它,也会直接输出hello world
shell
$ ./hello
hello world
库Package
我们再来创建一个库类型的Package
go
$ cargo new my_lib --lib
Created library `my_lib` package
但是如果你尝试使用cargo run
来运行,会直接报错
arduino
$ cargo run
error: a bin target must be available for `cargo run`
原因是库类型的 Package
只能作为三方库被其它项目引用,而不能独立运行,只有之前的二进制 Package
才可以运行。
与 src/main.rs
一样,Cargo 知道,如果一个 Package
包含有 src/lib.rs
,意味它包含有一个库类型的同名包 myLib
,该包的根文件是 src/lib.rs
。
Crate
Crate其实就是我们编译的基本单位,它也分成两种:可执行Crate和库Crate,如果和上面Package
两种类型对应的话,我觉得叫binary(application) Crate
和 library Crate
可能会好理解一点
可执行Crate
刚刚我们创建的hello
项目下的main.rs
其实就是一个可执行Crate文件,可以直接通过rustc
来编译
css
$ rustc main.rs
库Crate
就拿我们刚刚创建的库Package
myLib
举例子,src/lib.rs
就是一个库Crate
库Crate并没有 main
函数,它们也不会编译为可执行程序,它们提供一些诸如函数之类的东西,使其他项目也能使用这些东西。
rust
// src/lib.rs
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
我们使用cargo build
发现生成了两个libmy_lib
的文件,为什么叫libmy_lib
呢?因为这是library Crate
,所以叫lib
,还记得我们的Package
名字叫my_lib
吗,所以文件名就叫libmy_lib
了。与 src/main.rs
一样,Cargo 知道,如果一个 Package
包含有 src/lib.rs
,意味它包含有一个库类型的同名包 my_lib
,该包的根文件是 src/lib.rs
。
同时Rust规定,一个Package只会包含有一个或零个库Crate
如果我们想改默认的打包名呢?详见:doc.rust-lang.org/cargo/refer...
ini
[lib]
name = "foo" # The name of the target.
path = "src/lib.rs" # The source file of the target.
test = true # Is tested by default.
doctest = true # Documentation examples are tested by default.
bench = true # Is benchmarked by default.
doc = true # Is documented by default.
plugin = false # Used as a compiler plugin (deprecated).
proc-macro = false # Set to `true` for a proc-macro library.
harness = true # Use libtest harness.
edition = "2015" # The edition of the target.
crate-type = ["lib"] # The crate types to generate.
required-features = [] # Features required to build this target (N/A for lib).
Package和Crate规则
我们暂时先来总结一下
- 一个
Package
包含零个或一个库Crate
这点我们上面已经说过了,就不提拉~
- 一个
Package
可以包含任意多个可执行Crate
这个当然可以呀!src/main.rs
只是默认的可执行Crate
,我们还可以新建别的可执行Crate文件
比如我们在上述的Hello
Project中新建一个src/bin/test.rs
arduino
// src/bin.test.rs
fn main() {
println!("Hello, test!");
}
$ cargo run
error: `cargo run` could not determine which binary to run. Use the `--bin` option to specify a binary, or the `default-run` manifest key.
available binaries: hello, test
它会提示我们有两个可执行的Crate文件,分别是hello和test,我们需要去指定哪个文件
scss
$ cargo run --bin test
Compiling hello v0.1.0 (/Users/chenwenliang/rust/hello)
Finished dev [unoptimized + debuginfo] target(s) in 0.49s
Running `target/debug/test`
Hello, test!
- 一个
Package
至少包含一个库Crate
或者可执行Crate
这条规则也挺好理解的,我们之前提到Package
有两种类型,分别是二进制Package和库Package,对应的就是可执行Crate和库 Crate,说明Package必须是这两种类型中的一种
Module
我们讲完Package
和Crate
关系,接一下讲讲Crate
和Module
关系
一个Crate可以有多个Module,通过mod
关键字可以声明Module
调用mod
rust
// src/main.rs
mod hello {
pub fn hi() {
println!("hi");
}
pub fn bye() {
println!("bye")
}
pub mod say {
pub fn morning() {
println!("say morning")
}
pub fn night() {
println!("say night")
}
}
}
fn main() {
hello::bye();
// crate::hello::bye(); 也可以这么调用
hello::say::morning();
hello::hi();
hello::say::night();
println!("Hello, world!");
}
mod间相互调用
- 可以通过crate关键字,相当于从绝对路径开始找
rust
mod good {
pub fn log() {
// 调用hello mod中的函数
crate::hello::bye();
}
}
-
使用相对路径,从当前模块开始,以
self
、super
或当前模块的标识符开头- self: 自身
- super: 父级
rust
mod hello {
pub fn hi() {
println!("hi");
}
pub mod say {
pub fn good() {
// 自身mod
self::hi();
}
pub fn hi() {
// super代表上一级mod
super::hi();
}
}
}
不同Crate中mod调用
css
// src/main.rs
mod good
fn main() {
good::say_good();
}
我们可以在main.rs的同级目录下新建一个good.rs
文件,或者同级目录下新建good/mod.rs
因为main.rs中发现了声明mod good,它会在同级目录下去找good.rs文件,或者去找good文件下的mod.rs文件
rust
// src/good/mod.rs
// good.rs
pub fn say_good() {
println!("good");
}
注: 如果通过文件夹的方式,必须要以mod.rs命令,它会默认从这两个路径找
注:不同Crate引用要声明pub,不然会访问失败
调用library中的mod
我们怎么调用library Crate中的函数呢?我们知道lib默认的名字和Package的名字一样,所以我们直接使用Package的名字就可以调用library Crate中相应的函数
rust
// src/lib.rs
pub fn lib_hello() {
println!("lib hello")
}
// src/main.rs
fn main() {
hello::lib_hello();
println!("Hello, world!");
}