You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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-projectpackage, 同时在其中创建了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.rssrc/lib.rs都是编译单元,因此它们都是包。

典型的package结构

上面创建的package中仅包含src/main.rs文件,意味着它仅包含一个二进制同名包my-project。如果一个package同时拥有src/main.rssrc/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.rssrc/bin/main2.rs,它们会分别生成一个文件同名的二进制可执行文件
  • 集成测试文件:tests目录下
  • 性能测试benchmark文件: benches目录下
  • 项目示例: examples目录下

这种目录结构基本上是Rust的标准目录结构github的大多数项目上,你都将看到它的身影。

理解了包的概念,我们再来看看构成包的基本单元:模块。