Package & Crate

package 及 crate 是 Rust 的模块系统里的概念,一个 package 中可以包含一个或多个 crate ,而 crate 可以看成是 package 暴露的能力,crate 的表现可以是 library 或者 binary 。package 中可以通过 Cargo.toml 来告诉编译器如何构建其中的 crate ,一个 package 中至少要有一个 crate 。

可能会有点抽象,但是只要理解 crate 在项目中具体是什么就能理解了。可以执行下面这个指令

  1. $ cargo new mylib --lib

会自动创建一个 mylib 的项目,结构如下

  1. mylib
  2. ├── Cargo.toml
  3. └── src
  4. └── lib.rs

其中 mylib 就是 packge ,src/lib.rs就是同名的 library crate 。

此时也可以在 src 下加个 main.rs ,加了之后,mylib 这个 package 里就有两个 crate 了,一个是 src/lib.rs 作为 library crate,还有一个就是 main.rs 编译后产生的 binary crate( 即编译出来的二进制可执行文件 target/mylib )。用户使用这个 mylib 既可以使用 mylib 的 library 暴露的能力,也可以使用 mylib 里的 binary 。所以前面才会说,crate 可以认为是 package 暴露的能力。

一个 package 中只能包含一个 library crate( 因为 src/lib.rs 只能有一份呐 ),但是可以包含多个 binary crate ( src/main.rs 是一个,如果想要更多的 binary ,在 src/bin 目录下建的每一个 rs 文件都会编译成一个与文件名同名的 binray )

Modules

Rust 也支持通过模块的方式来组织代码,模块可以声明在同个文件里,如下

注意,只有加了 pub 前缀的函数,才会暴露出去。

  1. // src/lib.rs
  2. mod test_mod {
  3. pub fn test() -> u8 {
  4. 1
  5. }
  6. }
  7. pub fn test() -> u8 {
  8. let i = test_mod::test();
  9. i
  10. }
  11. #[cfg(test)]
  12. mod tests {
  13. use crate::test;
  14. #[test]
  15. fn it_works() {
  16. assert_eq!(test(), 1);
  17. }
  18. }

执行 cargo test 即可验证逻辑。上面的逻辑可以看到通过 :: 可以调用模块内部暴露的方法,同时也可以使用 use 来直接引入对应的函数,参考 tests 这个 module 下的 use crate::test ( crate 代表 library ),因为在 tests 中是直接 use 了 lib 下的 test 函数,所以后面的单测中就可以直接执行 test 方法了。

跑 cargo test 会自动执行 tests 这个模块下的所有测试

模块还可以定义到其他文件,比如上面的 test_mod 可以到 src/test_mod.rs

  1. // src/test_mod.rs
  2. pub fn test() -> u8 {
  3. 1
  4. }

lib.rs 里的代码就可以改成

  1. // src/lib.rs
  2. mod test_mod;
  3. pub fn test() -> u8 {
  4. let i = test_mod::test();
  5. i
  6. }

也可以用 use 把 test_mod 里的 test 方法引入进来,因为 lib 下就有个 test 了,所以可以用 as 将 test_mod 里的 test 换成其他名称。

  1. // src/lib.rs
  2. mod test_mod;
  3. use test_mod::test as test_mod_fn;
  4. pub fn test() -> u8 {
  5. let i = test_mod_fn();
  6. i
  7. }

test_mod 里也可以快速引入其他同名目录下的模块。比如新建一个 test_mod 目录,里面加个 test 函数

  1. // src/test_mod/sub_mod.rs
  2. pub fn test() -> u8 {
  3. 1
  4. }

那么在 test_mod.ts 中也可以便捷的引入该 module( 因为会查同名目录也就是 src/test_mod/ 下的模块 )

  1. // src/test_mod.rs
  2. mod sub_mod;
  3. pub fn test() -> u8 {
  4. sub_mod::test()
  5. }

在这些子模块里,也可以通过 crate::的方式,引入 library 中的其他模块,比如我在 lib.rs 有个 util 的模块。

  1. // src/lib.rs
  2. mod test_mod;
  3. mod util {
  4. pub fn test() -> u8 { 1 }
  5. }
  6. use test_mod::test as test_mod_fn;
  7. pub fn test() -> u8 {
  8. let i = test_mod_fn();
  9. i
  10. }

src/test_mod/sub_mod.rs 中就可以通过 crate 获取到这个 mod 并且进行使用

  1. // src/test_mod/sub_mod.rs
  2. pub fn test() -> u8 {
  3. crate::util::test()
  4. }

总结就是

  • lib.rs 中可以通过 mod xxx的方式引入 src 下的模块。
  • 子模块可以通过 crate::xxx 的方式获取到 lib.rs 中声明的模块。
  • 子模块可以通过 mod xxx的方式引入同名目录下的子模块( 如果要暴露子模块,需要 pub mod xxx )。