mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 08:51:18 +08:00
update ch14-02
This commit is contained in:
parent
559100881a
commit
f291adb581
@ -12,7 +12,7 @@ Rust 的 **闭包**(*closures*)是可以保存在一个变量中或作为参
|
||||
|
||||
有很多种方式来实现这些。例如,使用有 `Red` 和 `Blue` 两个成员的 `ShirtColor` 枚举(出于简单考虑限定为两种颜色)。我们使用 `Inventory` 结构体来代表公司的库存,它有一个类型为 `Vec<ShirtColor>` 的 `shirts` 字段表示库存中的衬衫的颜色。`Inventory` 上定义的 `giveaway` 方法获取免费衬衫得主所喜爱的颜色(如有),并返回其获得的衬衫的颜色。初始代码如示例 13-1 所示:
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
```rust,noplayground
|
||||
{{#rustdoc_include ../listings/ch13-functional-features/listing-13-01/src/main.rs}}
|
||||
@ -161,9 +161,9 @@ impl<T> Option<T> {
|
||||
|
||||
接着注意到 `unwrap_or_else` 函数有额外的范型参数 `F`。 `F` 是 `f` 参数(即调用 `unwrap_or_else` 时提供的闭包)的类型。
|
||||
|
||||
范型 `F` 的 trait bound 是 `FnOnce() -> T`,这意味着 `F` 必须能够被调用一次,没有参数并返回一个 `T`。在 trait bound 中使用 `FnOnce` 表示 `unwrap_or_else` 将最多调用 `f` 一次。 在 `unwrap_or_else` 的函数体中可以看到,如果 `Option` 是 `Some`,`f` 不会被调用。如果 `Option` 是 `None`,`f` 将会被调用一次。由于所有的闭包都实现了 `FnOnce`, `unwrap_or_else` 能接收绝大多数不同类型的闭包,十分灵活。
|
||||
范型 `F` 的 trait bound 是 `FnOnce() -> T`,这意味着 `F` 必须能够被调用一次,没有参数并返回一个 `T`。在 trait bound 中使用 `FnOnce` 表示 `unwrap_or_else` 将最多调用 `f` 一次。在 `unwrap_or_else` 的函数体中可以看到,如果 `Option` 是 `Some`,`f` 不会被调用。如果 `Option` 是 `None`,`f` 将会被调用一次。由于所有的闭包都实现了 `FnOnce`, `unwrap_or_else` 能接收绝大多数不同类型的闭包,十分灵活。
|
||||
|
||||
> 注意:函数也可以实现所有的三种 `Fn` traits。 如果我们要做的事情不需要从环境中捕获值,则可以在需要某种实现了 `Fn` trait 的东西时使用函数而不是闭包。举个例子,可以在 `Option<Vec<T>>` 的值上调用 `unwrap_or_else(Vec::new)` 以便在值为 `None` 时获取一个新的空的 vector。
|
||||
> 注意:函数也可以实现所有的三种 `Fn` traits。如果我们要做的事情不需要从环境中捕获值,则可以在需要某种实现了 `Fn` trait 的东西时使用函数而不是闭包。举个例子,可以在 `Option<Vec<T>>` 的值上调用 `unwrap_or_else(Vec::new)` 以便在值为 `None` 时获取一个新的空的 vector。
|
||||
|
||||
现在让我们来看定义在 slice 上的标准库方法 `sort_by_key`,看看它与 `unwrap_or_else` 的区别以及为什么 `sort_by_key` 使用 `FnMut` 而不是 `FnOnce` trait bound。这个闭包以一个 slice 中当前被考虑的元素的引用作为参数,返回一个可以用来排序的 `K` 类型的值。当你想按照 slice 中元素的某个属性来进行排序时这个函数很有用。在示例 13-7 中有一个 `Rectangle` 实例的列表,我们使用 `sort_by_key` 按 `Rectangle` 的 `width` 属性对它们从低到高排序:
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
> [ch14-00-more-about-cargo.md](https://github.com/rust-lang/book/blob/main/src/ch14-00-more-about-cargo.md)
|
||||
> <br>
|
||||
> commit c084bdd9ee328e7e774df19882ccc139532e53d8
|
||||
> commit 44e31f9f304e0cd9ace01045d17a2aa01a449528
|
||||
|
||||
目前为止我们只使用过 Cargo 构建、运行和测试代码这些最基本的功能,不过它还可以做到更多。本章会讨论 Cargo 其他一些更为高级的功能,我们将展示如何:
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
> [ch14-01-release-profiles.md](https://github.com/rust-lang/book/blob/main/src/ch14-01-release-profiles.md)
|
||||
> <br>
|
||||
> commit d44317c3122b44fb713aba66cc295dee3453b24b
|
||||
> commit 44e31f9f304e0cd9ace01045d17a2aa01a449528
|
||||
|
||||
在 Rust 中 **发布配置**(*release profiles*)是预定义的、可定制的带有不同选项的配置,他们允许程序员更灵活地控制代码编译的多种选项。每一个配置都彼此相互独立。
|
||||
|
||||
@ -19,7 +19,7 @@ $ cargo build --release
|
||||
|
||||
构建输出中的 `dev` 和 `release` 表明编译器在使用不同的配置。
|
||||
|
||||
当项目的 *Cargo.toml* 文件中没有任何 `[profile.*]` 部分的时候,Cargo 会对每一个配置都采用默认设置。通过增加任何希望定制的配置对应的 `[profile.*]` 部分,我们可以选择覆盖任意默认设置的子集。例如,如下是 `dev` 和 `release` 配置的 `opt-level` 设置的默认值:
|
||||
当项目的 *Cargo.toml* 文件中没有显式增加任何 `[profile.*]` 部分的时候,Cargo 会对每一个配置都采用默认设置。通过增加任何希望定制的配置对应的 `[profile.*]` 部分,我们可以选择覆盖任意默认设置的子集。例如,如下是 `dev` 和 `release` 配置的 `opt-level` 设置的默认值:
|
||||
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
@ -31,7 +31,7 @@ opt-level = 0
|
||||
opt-level = 3
|
||||
```
|
||||
|
||||
`opt-level` 设置控制 Rust 会对代码进行何种程度的优化。这个配置的值从 0 到 3。越高的优化级别需要更多的时间编译,所以如果你在进行开发并经常编译,可能会希望在牺牲一些代码性能的情况下编译得快一些。这就是为什么 `dev` 的 `opt-level` 默认为 `0`。当你准备发布时,花费更多时间在编译上则更好。只需要在发布模式编译一次,而编译出来的程序则会运行很多次,所以发布模式用更长的编译时间换取运行更快的代码。这正是为什么 `release` 配置的 `opt-level` 默认为 `3`。
|
||||
`opt-level` 设置控制 Rust 会对代码进行何种程度的优化。这个配置的值从 0 到 3。越高的优化级别需要更多的时间编译,所以如果你在进行开发并经常编译,可能会希望在牺牲一些代码性能的情况下减少优化以便编译得快一些。因此 `dev` 的 `opt-level` 默认为 `0`。当你准备发布时,花费更多时间在编译上则更好。只需要在发布模式编译一次,而编译出来的程序则会运行很多次,所以发布模式用更长的编译时间换取运行更快的代码。这正是为什么 `release` 配置的 `opt-level` 默认为 `3`。
|
||||
|
||||
我们可以选择通过在 *Cargo.toml* 增加不同的值来覆盖任何默认设置。比如,如果我们想要在开发配置中使用级别 1 的优化,则可以在 *Cargo.toml* 中增加这两行:
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
## 将 crate 发布到 Crates.io
|
||||
|
||||
> [ch14-02-publishing-to-crates-io.md](https://github.com/rust-lang/book/blob/main/src/ch14-02-publishing-to-crates-io.md) <br>
|
||||
> commit 7ddc28cfe0bfa6c531a6475c7fa41dfa66e8943c
|
||||
> commit 3f2a6ef48943ade3e9c0eb23d69e2b8b41f057f1
|
||||
|
||||
我们曾经在项目中使用 [crates.io](https://crates.io)<!-- ignore --> 上的包作为依赖,不过你也可以通过发布自己的包来向他人分享代码。[crates.io](https://crates.io)<!-- ignore --> 用来分发包的源代码,所以它主要托管开源代码。
|
||||
|
||||
Rust 和 Cargo 有一些帮助他人更方便找到和使用你发布的包的功能。我们将介绍一些这样的功能,接着讲到如何发布一个包。
|
||||
Rust 和 Cargo 有一些帮助他人更方便地找到和使用你发布的包的功能。我们将介绍一些这样的功能,接着讲到如何发布一个包。
|
||||
|
||||
### 编写有用的文档注释
|
||||
|
||||
@ -37,7 +37,7 @@ Rust 和 Cargo 有一些帮助他人更方便找到和使用你发布的包的
|
||||
- **Errors**:如果这个函数返回 `Result`,此部分描述可能会出现何种错误以及什么情况会造成这些错误,这有助于调用者编写代码来采用不同的方式处理不同的错误。
|
||||
- **Safety**:如果这个函数使用 `unsafe` 代码(这会在第十九章讨论),这一部分应该会涉及到期望函数调用者支持的确保 `unsafe` 块中代码正常工作的不变条件(invariants)。
|
||||
|
||||
大部分文档注释不需要所有这些部分,不过这是一个提醒你检查调用你代码的人有兴趣了解的内容的列表。
|
||||
大部分文档注释不需要所有这些部分,不过这是一个提醒你检查调用你代码的用户有兴趣了解的内容的列表。
|
||||
|
||||
#### 文档注释作为测试
|
||||
|
||||
@ -56,9 +56,9 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
|
||||
|
||||
#### 注释包含项的结构
|
||||
|
||||
还有另一种风格的文档注释,`//!`,这为包含注释的项,而不是位于注释之后的项增加文档。这通常用于 crate 根文件(通常是 _src/lib.rs_)或模块的根文件为 crate 或模块整体提供文档。
|
||||
文档注释风格 `//!` 为包含注释的项,而不是位于注释之后的项增加文档。这通常用于 crate 根文件(通常是 _src/lib.rs_)或模块的根文件为 crate 或模块整体提供文档。
|
||||
|
||||
作为一个例子,如果我们希望增加描述包含 `add_one` 函数的 `my_crate` crate 目的的文档,可以在 _src/lib.rs_ 开头增加以 `//!` 开头的注释,如示例 14-2 所示:
|
||||
作为一个例子,为了增加描述包含 `add_one` 函数的 `my_crate` crate 目的的文档,可以在 _src/lib.rs_ 开头增加以 `//!` 开头的注释,如示例 14-2 所示:
|
||||
|
||||
<span class="filename">文件名:src/lib.rs</span>
|
||||
|
||||
@ -68,7 +68,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
|
||||
|
||||
<span class="caption">示例 14-2:`my_crate` crate 整体的文档</span>
|
||||
|
||||
注意 `//!` 的最后一行之后没有任何代码。因为他们以 `//!` 开头而不是 `///`,这是属于包含此注释的项而不是注释之后项的文档。在这个情况中,包含这个注释的项是 _src/lib.rs_ 文件,也就是 crate 根文件。这些注释描述了整个 crate。
|
||||
注意 `//!` 的最后一行之后没有任何代码。因为他们以 `//!` 开头而不是 `///`,这是属于包含此注释的项而不是注释之后项的文档。在这个情况下时 _src/lib.rs_ 文件,也就是 crate 根文件。这些注释描述了整个 crate。
|
||||
|
||||
如果运行 `cargo doc --open`,将会发现这些注释显示在 `my_crate` 文档的首页,位于 crate 中公有项列表之上,如图 14-2 所示:
|
||||
|
||||
@ -80,10 +80,10 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
|
||||
|
||||
### 使用 `pub use` 导出合适的公有 API
|
||||
|
||||
第七章介绍了如何使用 `mod` 关键字来将代码组织进模块中,如何使用 `pub` 关键字将项变为公有,和如何使用 `use` 关键字将项引入作用域。然而你开发时候使用的文件架构可能并不方便用户。你的结构可能是一个包含多个层级的分层结构,不过这对于用户来说并不方便。这是因为想要使用被定义在很深层级中的类型的人可能很难发现这些类型的存在。他们也可能会厌烦要使用 `use my_crate::some_module::another_module::UsefulType;` 而不是 `use my_crate::UsefulType;` 来使用类型。
|
||||
|
||||
公有 API 的结构是你发布 crate 时主要需要考虑的。crate 用户没有你那么熟悉其结构,并且如果模块层级过大他们可能会难以找到所需的部分。
|
||||
|
||||
第七章介绍了如何使用 `mod` 关键字来将代码组织进模块中,如何使用 `pub` 关键字将项变为公有,和如何使用 `use` 关键字将项引入作用域。然而你开发时候使用的文件架构可能并不方便用户。你的结构可能是一个包含多个层级的分层结构,不过这对于用户来说并不方便。这是因为想要使用被定义在很深层级中的类型的人可能很难发现这些类型的存在。他们也可能会厌烦要使用 `use my_crate::some_module::another_module::UsefulType;` 而不是 `use my_crate::UsefulType;` 来使用类型。
|
||||
|
||||
好消息是,即使文件结构对于用户来说 **不是** 很方便,你也无需重新安排内部组织:你可以选择使用 `pub use` 重导出(re-export)项来使公有结构不同于私有结构。重导出获取位于一个位置的公有项并将其公开到另一个位置,好像它就定义在这个新位置一样。
|
||||
|
||||
例如,假设我们创建了一个描述美术信息的库 `art`。这个库中包含了一个有两个枚举 `PrimaryColor` 和 `SecondaryColor` 的模块 `kinds`,以及一个包含函数 `mix` 的模块 `utils`,如示例 14-3 所示:
|
||||
@ -114,7 +114,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
|
||||
|
||||
<span class="caption">示例 14-4:一个通过导出内部结构使用 `art` crate 中项的 crate</span>
|
||||
|
||||
示例 14-4 中使用 `art` crate 代码的作者不得不搞清楚 `PrimaryColor` 位于 `kinds` 模块而 `mix` 位于 `utils` 模块。`art` crate 的模块结构相比使用它的开发者来说对编写它的开发者更有意义。其内部的 `kinds` 模块和 `utils` 模块的组织结构并没有对尝试理解如何使用它的人提供任何有价值的信息。`art` crate 的模块结构因不得不搞清楚所需的内容在何处和必须在 `use` 语句中指定模块名称而显得混乱和不便。
|
||||
示例 14-4 中使用 `art` crate 代码的作者不得不搞清楚 `PrimaryColor` 位于 `kinds` 模块而 `mix` 位于 `utils` 模块。`art` crate 的模块结构相比使用它的开发者来说对编写它的开发者更有意义。其内部结构并没有对尝试理解如何使用 `art` crate 的人提供任何有价值的信息,相反因为不得不搞清楚所需的内容在何处和必须在 `use` 语句中指定模块名称而显得混乱。
|
||||
|
||||
为了从公有 API 中去掉 crate 的内部组织,我们可以采用示例 14-3 中的 `art` crate 并增加 `pub use` 语句来重导出项到顶层结构,如示例 14-5 所示:
|
||||
|
||||
@ -142,7 +142,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
|
||||
|
||||
<span class="caption">示例 14-6:一个使用 `art` crate 中重导出项的程序</span>
|
||||
|
||||
对于有很多嵌套模块的情况,使用 `pub use` 将类型重导出到顶级结构对于使用 crate 的人来说将会是大为不同的体验。
|
||||
对于有很多嵌套模块的情况,使用 `pub use` 将类型重导出到顶级结构对于使用 crate 的人来说将会是大为不同的体验。`pub use` 的另一个常见用法是重导出当前 crate 的依赖的定义使其 crate 定义变成你 crate 公有 API 的一部分。
|
||||
|
||||
创建一个有用的公有 API 结构更像是一门艺术而非科学,你可以反复检视他们来找出最适合用户的 API。`pub use` 提供了解耦组织 crate 内部结构和与终端用户体现的灵活性。观察一些你所安装的 crate 的代码来看看其内部结构是否不同于公有 API。
|
||||
|
||||
@ -156,11 +156,11 @@ $ cargo login abcdefghijklmnopqrstuvwxyz012345
|
||||
|
||||
这个命令会通知 Cargo 你的 API token 并将其储存在本地的 _~/.cargo/credentials_ 文件中。注意这个 token 是一个 **秘密**(**secret**)且不应该与其他人共享。如果因为任何原因与他人共享了这个信息,应该立即到 [crates.io](https://crates.io)<!-- ignore --> 重新生成这个 token。
|
||||
|
||||
### 发布新 crate 之前
|
||||
### 向新 crate 添加元信息
|
||||
|
||||
有了账号之后,比如说你已经有一个希望发布的 crate。在发布之前,你需要在 crate 的 _Cargo.toml_ 文件的 `[package]` 部分增加一些本 crate 的元信息(metadata)。
|
||||
比如说你已经有一个希望发布的 crate。在发布之前,你需要在 crate 的 _Cargo.toml_ 文件的 `[package]` 部分增加一些本 crate 的元信息(metadata)。
|
||||
|
||||
首先 crate 需要一个唯一的名称。虽然在本地开发 crate 时,可以使用任何你喜欢的名称。不过 [crates.io](https://crates.io)<!-- ignore --> 上的 crate 名称遵守先到先得的分配原则。一旦某个 crate 名称被使用,其他人就不能再发布这个名称的 crate 了。请在网站上搜索你希望使用的名称来找出它是否已被使用。如果没有,修改 _Cargo.toml_ 中 `[package]` 里的名称为你希望用于发布的名称,像这样:
|
||||
首先 crate 需要一个唯一的名称。虽然在本地开发 crate 时,可以使用任何你喜欢的名称。不过 [crates.io](https://crates.io)<!-- ignore --> 上的 crate 名称遵守先到先得的分配原则。一旦某个 crate 名称被使用,其他人就不能再发布这个名称的 crate 了。请搜索你希望使用的名称来找出它是否已被使用。如果没有,修改 _Cargo.toml_ 中 `[package]` 里的名称为你希望用于发布的名称,像这样:
|
||||
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
@ -183,11 +183,7 @@ Caused by:
|
||||
the remote server responded with an error: missing or empty metadata fields: description, license. Please see https://doc.rust-lang.org/cargo/reference/manifest.html for how to upload metadata
|
||||
```
|
||||
|
||||
这是因为我们缺少一些关键信息:关于该 crate 用途的描述和用户可能在何种条款下使用该 crate 的 license。为了修正这个错误,需要在 _Cargo.toml_ 中引入这些信息。
|
||||
|
||||
描述通常是一两句话,因为它会出现在 crate 的搜索结果中和 crate 页面里。对于 `license` 字段,你需要一个 **license 标识符值**(_license identifier value_)。[Linux 基金会的 Software Package Data Exchange (SPDX)][spdx] 列出了可以使用的标识符。例如,为了指定 crate 使用 MIT License,增加 `MIT` 标识符:
|
||||
|
||||
[spdx]: http://spdx.org/licenses/
|
||||
这个错误是因为我们缺少一些关键信息:关于该 crate 用途的描述和用户可能在何种条款下使用该 crate 的 license。在 _Cargo.toml_ 中添加描述,通常是一两句话,因为它会出现在 crate 的搜索结果中和 crate 页面里。对于 `license` 字段,你需要一个 **license 标识符值**(_license identifier value_)。[Linux 基金会的 Software Package Data Exchange (SPDX)][spdx] 列出了可以使用的标识符。例如,为了指定 crate 使用 MIT License,增加 `MIT` 标识符:
|
||||
|
||||
<span class="filename">文件名:Cargo.toml</span>
|
||||
|
||||
@ -243,24 +239,29 @@ $ cargo publish
|
||||
|
||||
当你修改了 crate 并准备好发布新版本时,改变 _Cargo.toml_ 中 `version` 所指定的值。请使用 [语义化版本规则][semver] 来根据修改的类型决定下一个版本号。接着运行 `cargo publish` 来上传新版本。
|
||||
|
||||
[semver]: http://semver.org/
|
||||
|
||||
### 使用 `cargo yank` 从 Crates.io 撤回版本
|
||||
### 使用 `cargo yank` 从 Crates.io 弃用版本
|
||||
|
||||
虽然你不能删除之前版本的 crate,但是可以阻止任何将来的项目将他们加入到依赖中。这在某个版本因为这样或那样的原因被破坏的情况很有用。对于这种情况,Cargo 支持 **撤回**(_yanking_)某个版本。
|
||||
|
||||
撤回某个版本会阻止新项目开始依赖此版本,不过所有现存此依赖的项目仍然能够下载和依赖这个版本。从本质上说,撤回意味着所有带有 _Cargo.lock_ 的项目的依赖不会被破坏,同时任何新生成的 _Cargo.lock_ 将不能使用被撤回的版本。
|
||||
撤回某个版本会阻止新项目依赖此版本,不过所有现存此依赖的项目仍然能够下载和依赖这个版本。从本质上说,撤回意味着所有带有 _Cargo.lock_ 的项目的依赖不会被破坏,同时任何新生成的 _Cargo.lock_ 将不能使用被撤回的版本。
|
||||
|
||||
为了撤回一个 crate,运行 `cargo yank` 并指定希望撤回的版本:
|
||||
为了撤回一个版本的 crate,在之前发布 crate 的目录运行 `cargo yank` 并指定希望撤回的版本。例如,如果我们发布了一个名为 `guessing_game` 的 crate 的 1.0.1 版本并希望撤回它,在 `guessing_game` 项目目录运行:
|
||||
|
||||
```console
|
||||
$ cargo yank --vers 1.0.1
|
||||
Updating crates.io index
|
||||
Yank guessing_game@1.0.1
|
||||
```
|
||||
|
||||
也可以撤销撤回操作,并允许项目可以再次开始依赖某个版本,通过在命令上增加 `--undo`:
|
||||
|
||||
```console
|
||||
$ cargo yank --vers 1.0.1 --undo
|
||||
Updating crates.io index
|
||||
Unyank guessing_game@1.0.1
|
||||
```
|
||||
|
||||
撤回 **并没有** 删除任何代码。举例来说,撤回功能并不意在删除不小心上传的秘密信息。如果出现了这种情况,请立即重新设置这些秘密信息。
|
||||
撤回 **并没有** 删除任何代码。举例来说,撤回功能并不能删除不小心上传的秘密信息。如果出现了这种情况,请立即重新设置这些秘密信息。
|
||||
|
||||
[spdx]: http://spdx.org/licenses/
|
||||
[semver]: http://semver.org/
|
||||
|
Loading…
Reference in New Issue
Block a user