Rust 学习 12

archive time: 2022-06-23

这回来看看 cargo 打包

Rust 有一个很大的优势就是它有一个完整的完善的工具链, cargo

通过 cargo, 我们可以很轻松的完成编译, 测试, 打包这几个步骤

不过关于 cargo 打包, 有一些内容还是需要主要

release profile

profile 是预先定义好的相关的配置, 对应的有 release 和 dev 两种

每个 profile 都是可以自行配制的, 就是在 Cargo.toml 里设置

# Cargo.toml
# ...
[profile.dev]
opt-level = 0

[profile.release]
opt-level = 3

这样我们就可以覆盖默认的 profile 里的 opt-level 设置了

至于有哪些选项, 可以参考文档1

发布 Crate

我们除了可以下载 crate, 我们还可以上传我们的 crate

文档注释

在 crate 里, 文档是很重要的, 在 Rust 里, 专门有种注释来生产文档, 那就是 文档注释

文档注释 用于生成 HTML 文档, 显式公共 API 的注释还可以说明如何使用 API

甚至在 Rust 还可以进行文档测试, 即在文档里测试代码, 以保证文档和代码同步以及代码或文档的正确性

文档注释使用 /// 来实现, 支持一定的 Markdown 语法

/// Adds one to the number given.
///
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = learn_rust::add_one(arg);
///
/// assert_eq!(6, answer);
/// ```
pub fn add_one(x: i32) -> i32 {
    x + 1
}

效果如下图

doc comment

我们可以使用 cargo doc 来生成文档

注意到在项目目录下的 target/ 目录下有了一个 doc/ 目录, 里面就是生成的文档

(base) PS projRoot> dir .\target\doc\


    目录: projRoot\target\doc


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         2022/6/23     14:14                cfg_if
d-----         2022/6/23     14:14                either
d-----         2022/6/23     14:14                getrandom
d-----         2022/6/23     14:14                implementors
d-----         2022/6/23     14:14                itertools
d-----         2022/6/23     14:14                learn_rust
d-----         2022/6/23     14:14                ppv_lite86
d-----         2022/6/23     14:14                rand
d-----         2022/6/23     14:14                rand_chacha
d-----         2022/6/23     14:14                rand_core
d-----         2022/6/23     14:14                src
-a----         2022/6/23     14:14              0 .lock
-a----         2022/6/23     14:14          12235 ayu.css
-a----         2022/6/23     14:14            576 clipboard.svg
-a----         2022/6/23     14:14           1791 COPYRIGHT.txt
-a----         2022/6/23     14:14            123 crates.js
-a----         2022/6/23     14:14          10683 dark.css
-a----         2022/6/23     14:14            510 down-arrow.svg
-a----         2022/6/23     14:14            715 favicon-16x16.png
-a----         2022/6/23     14:14           1125 favicon-32x32.png
-a----         2022/6/23     14:14           4298 favicon.svg
-a----         2022/6/23     14:14           4419 FiraSans-LICENSE.txt
-a----         2022/6/23     14:14         132780 FiraSans-Medium.woff2
-a----         2022/6/23     14:14         129188 FiraSans-Regular.woff2
-a----         2022/6/23     14:14          10847 LICENSE-APACHE.txt
-a----         2022/6/23     14:14           1023 LICENSE-MIT.txt
-a----         2022/6/23     14:14          10287 light.css
-a----         2022/6/23     14:14          19656 main.js
-a----         2022/6/23     14:14           4745 NanumBarunGothic-LICENSE.txt
-a----         2022/6/23     14:14         399468 NanumBarunGothic.ttf.woff2
-a----         2022/6/23     14:14           1853 normalize.css
-a----         2022/6/23     14:14             97 noscript.css
-a----         2022/6/23     14:14           3297 rust-logo.svg
-a----         2022/6/23     14:14          28317 rustdoc.css
-a----         2022/6/23     14:14         164833 search-index.js
-a----         2022/6/23     14:14          34753 search.js
-a----         2022/6/23     14:14           1425 settings.css
-a----         2022/6/23     14:14           3792 settings.html
-a----         2022/6/23     14:14           6064 settings.js
-a----         2022/6/23     14:14           2019 source-files.js
-a----         2022/6/23     14:14           4403 source-script.js
-a----         2022/6/23     14:14          44896 SourceCodePro-It.ttf.woff2
-a----         2022/6/23     14:14           4528 SourceCodePro-LICENSE.txt
-a----         2022/6/23     14:14          52228 SourceCodePro-Regular.ttf.woff2
-a----         2022/6/23     14:14          52348 SourceCodePro-Semibold.ttf.woff2
-a----         2022/6/23     14:14          81320 SourceSerif4-Bold.ttf.woff2
-a----         2022/6/23     14:14          59860 SourceSerif4-It.ttf.woff2
-a----         2022/6/23     14:14           4485 SourceSerif4-LICENSE.md
-a----         2022/6/23     14:14          76180 SourceSerif4-Regular.ttf.woff2
-a----         2022/6/23     14:14           3556 storage.js
-a----         2022/6/23     14:14            174 toggle-minus.svg
-a----         2022/6/23     14:14            191 toggle-plus.svg
-a----         2022/6/23     14:14           3764 wheel.svg

<projRoot>/target/doc/<projName>/index.html 就是文档的主页面了

打开后可以看到类似这样的页面, 一个很标准的 Rust 文档界面

standard doc pages

当然, 除了手动打开, 我们可以使用 cargo doc --open 来打开页面, 效果是一样的

在文档注释里面一般有几个固定的区域

  • Examples: 示例, 说明函数或代码的用法
  • Panics: 可能发生 panic 的场景
  • Errors: 返回 Result 或 Option 时的错误类型或者产生错误的条件
  • Safty: 如果函数是 unsafe 的, 说明原因, 以及使用前提

文档测试

我们在文档里写的例子可以进行文档测试, 使用 cargo test --doc 即可

(base) PS projRoot> cargo test --doc
    Finished test [optimized + debuginfo] target(s) in 0.03s
   Doc-tests learn_rust

running 1 test
test src\lib.rs - add_one (line 122) ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.21s

注释文档

除了为文档下的代码进行注释, 我们还可以对整个文件或者 crate 或者 模块 进行注释, 使用符号 //!

// src/lib.rs

//! # Learn Rust
//!
//! `learn_rust` 是我学习 Rust 时的一个测试项目, 方便进行测试

// ....

src/lib.rs 里添加了这么一些注释后, 我们再打开文档, 发现文档内容也发生了变化

after doc

pub use

有时候我们需要为方便调用来导出一些公共 API, 这样就可以使用 pub use 关键字

pub use 作用是 重新导出, 创建了一个与内部私有结构不同的对外公共结构

假设我们有这么一个文件 src/lib.rs

//! # Learn Rust
//!
//! `learn_rust` 是我学习 Rust 时的一个测试项目, 方便进行测试

pub mod kinds {
    /// The primary colors according to the RYB color model
    pub enum PrimaryColor {
        Red,
        Yellow,
        Blue,
    }

    /// The secondary colors according to the RYB color model
    pub enum SecondaryColor {
        Orange,
        Green,
        Purple,
    }
}

pub mod utils {
    use crate::kinds::*;

    /// Combines two primary colors in equal amounts to create
    /// a secondary color
    pub fn mix(color1: PrimaryColor, color2: PrimaryColor) -> SecondaryColor {
        SecondaryColor::Green
    }
}

在文档里表现出来是这样的

test doc

不是很直观, 也不清楚每个模块里的的东西

如果我们在文件里加上这几行

// src/lib.rs

pub use self::kinds::PrimaryColor;
pub use self::kinds::SecondaryColor;
pub use self::utils::mix;

// ...

再看一眼文档, 会出现一个 Re-exports 栏目, 里面摆上了我们重新导出的内容, 就很直观了

after pub use

在使用时, 我们也只需要 <projName>::<itemName> 即可, itemName 是你导出的内容的名字

创建 crates.io 账号

前面部分我们知道了如何写文档注释和生成文档, 现在来正式看看如何发布

再发布之前我们还需要有一个 crates.io 的账号, 这是自然的

注意看 crates.io 的主界面 (2022 年 06 月 23 日), 我们可以看到这么一个选项

crates.io

注册好账号后, 我们就有了一个 crates.io 的 API Token

点击 Account Settings, 我们就可以看到一个 API Tokens 的地方

some steps

然后我们就可以生成一个新的 token 供我们使用了

generate token

有了 token, 我们就可以在 cargo 使用 cargo login 来进行登录操作

(base) PS projRoot> cargo login <API Token>
       Login token for `crates.io` saved

API Token 会被存放在 <cargo-home>/credentials 里面

不过在发布前还是需要在我们的 crate 里的 [package] 添加对应元数据

  • name: crate 的唯一标识
  • description: 对 crate 的简单描述
  • license: 许可证的标识值2, 多个许可使用 OR 隔开
  • version: 版本
  • authors: 作者, 一个列表, 可以放多个名字

例如对于 learn_rust 我们可以这样

[package]
name = "learn_rust"
version = "0.1.0"
edition = "2021"
license = "MIT"
authors = ["kands-code"]

确保一切准备妥当, 我们就可以使用 cargo publish 来发布 crate 了

发布之后

发布 crate 之后, 我们是 不能够 删除 crate 的, 不过我们可以 撤回(yank) 一个 crate 版本

yank 可以防止新项目依赖于该版本, 但是无法阻止继续使用其作为依赖

yank 了就意味着

  • 所有产生 Cargo.lock 的项目都不会中断
  • 任何将来生成的 Cargo.lock 文件都不会使用被 yank 的版本
cargo yank --vers <version>

我们还可以取消 yank

cargo yank --vers <version> --undo

Cargo 工作空间

有些时候, 为了模块化项目, 我们需要将项目拆成多个 crate

而工作空间就是一套共享 Cargo.lock 和 输出文件夹 的包

对于作为工作空间的目录, 我们需要准备一个 Cargo.toml 文件, 里面设置工作空间

[workspace]
members = ["adder", "add-one"]

然后在目录下使用 cargo new addercargo new add-one --lib 来创建这两个成员

我们使用 cargo build 发现, 在 adder/add-one/ 目录下都没有 target/ 目录, 而是在工作空间目录有一个统一的 target/

(base) PS ws> dir .

    Directory: ws

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----           2022/6/23    16:17                add-one
d----           2022/6/23    16:15                adder
d----           2022/6/23    16:14                target
-a---           2022/6/23    16:14              8 .gitignore
-a---           2022/6/23    16:18            197 Cargo.lock
-a---           2022/6/23    16:18             45 Cargo.toml

如果需要说明这两个 crate 相互依赖, 需要在对应的 crate 里说明

# adder/Cargo.toml
[package]
name = "adder"
version = "0.1.0"
edition = "2021"

[dependencies]
add-one = { path = "../add-one" }

我们如果要执行工作空间中某一特定 crate, 则需要 cargo run -p <crate-name>

(base) PS ws> cargo run -p adder
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target\debug\adder.exe`
31

工作空间的一个很重要的意义就是同步版本, 因为一个工作空间内的 crate 都共用一个 Cargo.lock, 保证了使用的库的版本统一

这样一来, 一个工作空间内的 crate 都是相互兼容的

安装二进制 crate

我们可以从 crates.io 中安装二进制 crate, 使用 cargo install <crate-name> 即可

这个就无需演示了

扩展 cargo

如果二进制名称为 cargo-xxx, 则我们可以使用 cargo xxx 来执行这个二进制名字, 看起来就是扩展了 cargo 的功能


好, cargo 就算学得差不多了, 开摸!

1

相关文档 Cargo.toml, Profiles

2

可以在 这里 查找, 对应 identifier 一栏