cargo之强大,怕是无出其右者了,而且易学易用。

所以这篇文章就来讲下快速入门cargo。

依赖管理

来自 crates.io 的crate:

# ...

[dependencies]
romio = "0.3.0-alpha.2"
futures-preview = "0.3.0-alpha.13"

来自github的crate

# ...

[dependencies]
# 指定repo路径,还有branch
romio = { git = "https://github.com/withoutboats/romio", branch = "master" }
futures-preview = "0.3.0-alpha.13"

来自本地的crate

# ...

[dependencies]
# 相对路径
romio = "../romio"
futures-preview = "0.3.0-alpha.13"

多个子crate管理

当 crate 比较大型且复杂的时候,就有必要分离一些子项目了,既便于管理这个crate,也给予开发很多弹性。

# ...

# 确保在cargo.toml同级目录下,有这些子crate文件夹存在
[workspace]
members = [
  "sub_crate1",
  "sub_crate2",
  "sub_crate3",
  "sub_crate4",
]

# ...

feature 管理

cargo可以方便地管理每一个数据结构,函数,trait,定义他们是哪个feature,然后在编译时,或者在cargo.toml中配置。

[features]
default = ["feature1"]
feature1 = []
feature2 = []
# ...

而代码中是这样写的:

#[cfg(feature="feature1")]
pub fn test1() {
    // ...
}

#[cfg(feature="feature2")]
pub fn test2() {
    // ...
}

默认地,函数 test2是不会被编译的,但test1会被编译,因为 feature1 是默认的feature。

但要如何开启feature2呢? 有两种办法:

  • 配置cargo.toml文件。
[features]
default = ["feature1", "feature2"]
feature1 = []
feature2 = []
# ...
  • 编译时指定feature。
$ cargo run --features "feature2"

自定义编译工程

cargo支持自定义编译工程,通过 build.rs 文件来实现。

比如编译crate依赖的第三方库,是用C/C++编写的,这个时候就需要 build.rs 来协助编译了。具体的使用,可以看官方文档:

也可以看我之前写的一篇文章 如何在Rust里通过FFI调用CUDA库,里面有使用到。

发布crate到 crates.io

如果你编写的crate对别人有帮助,也愿意公开,可以发布到 crates.io

关于这方面的文档,可以看这个文档,这里就不细说了。

cargo的内置常用命令

cargo command

  • cargo new. 创建工程。
  • cargo bench. 基准测试。
  • cargo build. 编译工程
  • cargo check. 检查当前工程,还有依赖的crate,是否有错误。这个命令还会保存当前工程的元数据(metadata),以备将来利用。
  • cargo clean. 删除生成的target。
  • cargo doc. 构建当前工程的文档。
  • cargo fetch. 获取依赖的crate。
  • cargo fix. 自动修复编译器给出的 lint 警告。
  • cargo run. 运行当前工程。
  • cargo test. 执行工程的测试用例。
  • cargo install. 安装cargo plugin。

cargo的扩展性

cargo支持扩展,可以为cargo提供第三方的plugin。具体的可以看我之前写的一篇文章 如何为cargo写一个插件(plugin)

Misc

说了这么多,来看下完整的 cargo.toml 文件内容吧。

拿一个比较出名的crate来举例,时间处理包 chrono

[package] # crate的一些元信息
name = "chrono" # crate名字,必须的
version = "0.4.6" # 版本好,必须的

# 以下都不是必须的字段,但可以更加清楚地描述这个crate
authors = [
    "Kang Seonghoon <public+rust@mearie.org>",
    "Brandon W Maister <quodlibetor@gmail.com>",
] # 作者

description = "Date and time library for Rust" # crate描述
homepage = "https://github.com/chronotope/chrono" # crate主页地址
documentation = "https://docs.rs/chrono/" # crate文档地址
repository = "https://github.com/chronotope/chrono" # crate的repo地址
keywords = ["date", "time", "calendar"] # crate中的一些关键字
categories = ["date-and-time"] # crate归类,如果上传到 crates.io,会被归类到 date-and-time
readme = "README.md" # crate的ReadMe文档
license = "MIT/Apache-2.0" # crate的license
exclude = ["/ci/*", "/.travis.yml", "/appveyor.yml", "/Makefile"] # 当构建crate时,这些类型的文件夹,文件名等不会包含进来

# 多加了几个字段,也是不必须的
build = "build.rs" # 自定义编译crate,比如编译依赖的 C++库
edition = "2018" # 指定rust版本,either 2018 or 2015,不指定就是2015版本
links = "foo" # crate要链接的库名
include = ["src/**/*", "Cargo.toml"] # 当构建crate时,指定包含的文件夹,文件类型等
publish = false # 是否发布到 crates.io

[badges] 
# 貌似没什么用,给第三方指定badges,travis-ci,codecov这些badges,在repo的首页能经常看到
travis-ci = { repository = "chronotope/chrono" }
appveyor = { repository = "chronotope/chrono" }

[lib]
name = "chrono" # 生成库的名字

[features] # crate的一些特性设置
default = ["clock"] # 默认特性,编译时会被默认支持
clock = ["time"] # 定义了一个 time feature

[dependencies] # crate的依赖
time = { version = "0.1.39", optional = true }
num-integer = { version = "0.1.36", default-features = false }
num-traits = { version = "0.2", default-features = false }
rustc-serialize = { version = "0.3.20", optional = true }
serde = { version = "1", optional = true }

[dev-dependencies] 
# 开发时的依赖,比如做测试,基准测试,example的依赖,
# 发布的时候,依赖的这些 crates 不会被编译进来
serde_json = { version = "1" }
serde_derive = { version = "1" }
bincode = { version = "0.8.0" }
num-iter = { version = "0.1.35", default-features = false }

[package.metadata.docs.rs]
# 一般crate默认的feature文档是可以被构建的,但不是默认的就不回构建文档,
# 设置成 true,可以构建所以features的文档,而不仅仅是默认features的文档
all-features = true

[package.metadata.playground]
# rust有个playground,会给予下载量前100名支持,所以如果crate进入了100名,
# 最好是开启所有的 features,而不仅仅是默认的,playground是无法配置crate的 feature
all-features = true

结语

这里只是讲了cargo快速入门的一些必要知识点,其实还有很多功能,没有列举出来。详细的cargo使用,去看官方的文档

而且cargo还在不断进化中,会越来越强大!