5.5 KiB
包和Package
当读者按照章节顺序读到本章时,意味着你已经几乎具备了参与真实项目开发的能力。但是真实项目远比我们之前的cargo new
的默认目录结构要复杂,好在,Rust为我们提供了强大的包管理工具:
- Package: 可以用来构建、测试和分享包
- 工作空间workspace: 对于大型项目,可以进一步将多个包联合在一起,组织成工作空间
- 包Crate: 一个由多个模块组成的树形结构,可以作为三方库进行分发,也可以生成可执行文件进行运行
- 模块Module:可以一个文件多个模块,也可以一个文件一个模块,模块可以被认为是真实项目中的代码组织单元
定义
其实Package
和包很容易被搞混,甚至在很多书中,这两者都是不分的,但是由于官方对此做了明确的区分,因此我们会在本章节中试图(挣扎着)理清这个概念.
包crate
对于Rust而言,包是一个独立的可编译单元,它编译后会生成一个可执行文件或者一个库。
一个包会将相关联的功能打包在一起,使得该功能可以很方便的在多个项目中分享。例如标准库中没有提供但是在三方库中提供的rand
包,它提供了随机数生成的功能,我们只需要将该包通过use rand;
引入到当前项目的作用域中,就可以在项目中使用rand
的功能: rand::XXX
。
同一个包中不能有同名的类型,但是在不同包中就可以。例如,虽然rand
包中,有一个Rng
特征,可是我们依然可以在自己的项目中定义一个Rng
,前者通过rand::Rng
访问,后者通过Rng
访问,对于编译器而言,这两者的边界非常清晰,不会存在引用歧义。
Package
鉴于Rust团队标新立异的起名传统,以及包的名称被crate
占用,库的名称被library
占用,因此导致我不知道该如何准确的翻译Package
,遂决定,不翻译,但是大家可以把它理解为一个项目工程,或者一个微服务工程。
由于Package
就是一个项目,因此它包含有独立的Cargo.toml
文件, 以及因为功能性被组织在一起的一个或多个包。一个package
只能包含一个库(library)类型的包, 但是可以包含多个二进制可执行类型的包。
二进制package
让我们来创建一个二进制package
:
$ cargo new my-project
Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs
这里,Cargo为我们创建了一个名称是my-project
的package
, 同时在其中创建了Cargo.toml
文件,可以看一下该文件,里面并没有提到src/main.rs
作为程序的入口, 原因是Cargo有一个惯例:src/main.rs是二进制包的根文件,该二进制包的包名跟所属package相同,在这里都是my-project, 所有的代码执行都从该文件中的fn main()
函数开始。
使用cargo run
可以运行该项目,输出:Hello, world!
.
库package
再来创建一个库类型的package
:
$ cargo new my-test --lib
Created library `my-lib` package
$ ls my-lib
Cargo.toml
src
$ ls my-lib/src
lib.rs
首先,如果你试图运行my-test
,会报错:
$ cargo run
error: a bin target must be available for `cargo run`
原因是库类型的package
只能作为三方库被其它项目引用,而不能独立运行,只有之前的二进制package
才可以运行。
与src/main.rs
一样,Cargo知道,如果一个package
包含有src/lib.rs
,意味它包含有一个库类型的同名包my-lib
,该包的根文件是src/lib.rs
易混淆的package和包
看完上面,相信大家看出来为何package
和包容易被混淆了吧?因为你用cargo new
创建的package
和它其中包含的包是同名的!
不过,只要你牢记package
是一个项目工程,而包只是一个编译单元,基本上也就不会混淆这个两个概念了: src/main.rs
和src/lib.rs
都是编译单元,因此它们都是包。
典型的package
结构
上面创建的package
中仅包含src/main.rs
文件,意味着它仅包含一个二进制同名包my-project
。如果一个package
同时拥有src/main.rs
和src/lib.rs
,那就意味着它包含两个包:库包和二进制包,这两个包名也都是my-project
—— 都与package
同名。
一个真实项目中典型的package
,会包含多个二进制包,这些包文件被放在src/bin
目录下,每一个文件都是独立的二进制包,同时也会包含一个库包,该包只能存在一个src/lib.rs
:
.
├── Cargo.toml
├── Cargo.lock
├── src
│ ├── main.rs
│ ├── lib.rs
│ └── bin
│ └── main1.rs
│ └── main2.rs
├── tests
│ └── some_integration_tests.rs
├── benches
│ └── simple_bench.rs
└── examples
└── simple_example.rs
- 唯一库包,
src/lib.rs
- 默认二进制包,
src/main.rs
,编译后生成的可执行文件与package
同名 - 其余二进制包,
src/bin/main1.rs
和src/bin/main2.rs
,它们会分别生成一个文件同名的二进制可执行文件 - 集成测试文件:
tests
目录下 - 性能测试benchmark文件:
benches
目录下 - 项目示例:
examples
目录下
这种目录结构基本上是Rust的标准目录结构,在github
的大多数项目上,你都将看到它的身影。
理解了包的概念,我们再来看看构成包的基本单元:模块。