check ch14-03

pull/79/head
KaiserY 7 years ago
parent 13882b1c09
commit fe2455cb0d

@ -4,9 +4,9 @@
> <br> > <br>
> commit d06a6a181fd61704cbf7feb55bc61d518c6469f9 > commit d06a6a181fd61704cbf7feb55bc61d518c6469f9
第二章中提到过,变量默认是 **不可变***immutable*)的。这是利用 Rust 安全和简单并发的优势编写代码一大助力。不过,变量仍然有可变的选项。让我们探讨一下拥抱不可变性的原因及方法,以及何时你不想使用不可变性。 第二章中提到过,变量默认是 **不可变***immutable*)的。这是利用 Rust 安全和简单并发的优势编写代码一大助力。不过,变量仍然有可变的选项。让我们探讨一下 Rust 拥抱不可变性的原因及方法,以及何时你不想使用不可变性。
当变量不可变时,意味着一旦值被绑定上一个名称,你就不能改变这个值。作为说明,通过`cargo new --bin variables` 在 *projects* 目录生成一个叫做 *variables* 的新项目。 当变量不可变时,意味着一旦值被绑定上一个名称,你就不能改变这个值。作为说明,通过 `cargo new --bin variables`*projects* 目录生成一个叫做 *variables* 的新项目。
接着,在新建的 *variables* 目录,打开 *src/main.rs* 并替换其代码为如下: 接着,在新建的 *variables* 目录,打开 *src/main.rs* 并替换其代码为如下:
@ -34,7 +34,7 @@ error[E0384]: re-assignment of immutable variable `x`
| ^^^^^ re-assignment of immutable variable | ^^^^^ re-assignment of immutable variable
``` ```
这个例子展示了编译器如何帮助你找出程序中的错误。即便编译错误令人沮丧,那也不过是说程序不能安全的完成你想让它完成的工作;而 **不能** 说明你是不是一个好程序员!有经验的 Rustacean 们一样会遇到编译错误。这些错误给出的原因是 `对不可变变量重新赋值``re-assignment of immutable variable`),因为我们尝试对不可变变量 `x` 赋第二个值。 这个例子展示了编译器如何帮助你找出程序中的错误。虽然编译错误令人沮丧,那也不过是说程序不能安全的完成你想让它完成的工作;而 **不能** 说明你是不是一个好程序员!有经验的 Rustacean 们一样会遇到编译错误。这些错误给出的原因是 `对不可变变量重新赋值``re-assignment of immutable variable`),因为我们尝试对不可变变量 `x` 赋第二个值。
尝试去改变预设为不可变的值,产生编译错误是很重要的,因为这种情况可能导致 bug如果代码的一部分假设一个值永远也不会改变而另一部分代码改变了它第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 难以跟踪,尤其是第二部分代码只是 **有时** 改变其值。 尝试去改变预设为不可变的值,产生编译错误是很重要的,因为这种情况可能导致 bug如果代码的一部分假设一个值永远也不会改变而另一部分代码改变了它第一部分代码就有可能以不可预料的方式运行。不得不承认这种 bug 难以跟踪,尤其是第二部分代码只是 **有时** 改变其值。
@ -120,14 +120,14 @@ The value of x is: 12
这与将变量声明为 `mut` 是有区别的。因为除非再次使用 `let` 关键字,不小心尝试对变量重新赋值会导致编译时错误。我们可以用这个值进行一些计算,不过计算完之后变量仍然是不变的。 这与将变量声明为 `mut` 是有区别的。因为除非再次使用 `let` 关键字,不小心尝试对变量重新赋值会导致编译时错误。我们可以用这个值进行一些计算,不过计算完之后变量仍然是不变的。
`mut` 与隐藏的另一个区别是,当再次使用 `let` 时,实际上创建了一个新变量,我们可以改变值的类型,从而用这个名字。例如,假设程序请求用户输入空格来提供文本间隔,然而我们真正需要的是将输入存储成数字(多少个空格): `mut` 与隐藏的另一个区别是,当再次使用 `let` 时,实际上创建了一个新变量,我们可以改变值的类型,从而用这个名字。例如,假设程序请求用户输入空格来提供文本间隔,然而我们真正需要的是将输入存储成数字(多少个空格):
```rust ```rust
let spaces = " "; let spaces = " ";
let spaces = spaces.len(); let spaces = spaces.len();
``` ```
这里允许第一个 `spaces` 变量是字符串类型,而第二个 `spaces` 变量,它是一个恰巧与第一个变量同名的崭新变量,是数字类型。隐藏使我们不必使用不同的名字,如 `spaces_str``spaces_num`;相反,我们可以复用 `spaces` 这个更简单的名字。然而,如果尝试使用`mut`,如下所示: 这里允许第一个 `spaces` 变量是字符串类型,而第二个 `spaces` 变量,它是一个恰巧与第一个变量同名的崭新变量,是数字类型。隐藏使我们不必使用不同的名字,如 `spaces_str``spaces_num`;相反,我们可以复用 `spaces` 这个更简单的名字。然而,如果尝试使用 `mut`,如下所示:
```rust,ignore ```rust,ignore
let mut spaces = " "; let mut spaces = " ";

@ -34,37 +34,81 @@ pub fn add_one(x: i32) -> i32 {
<span class="caption">列表 14-2一个函数的文档注释</span> <span class="caption">列表 14-2一个函数的文档注释</span>
`cargo doc`运行一个由 Rust 分发的工具,`rustdoc`,来为这些注释生成 HTML 文档。可以运行`cargo doc --open`在本地尝试一下,这会构建当前状态的文档(以及 crate 的依赖)并在浏览器中打开。导航到`add_one`函数将会发现文档注释是如何渲染的 这里,我们提供了一个 `add_one` 函数工作的描述,接着开始了一个标题为 “Examples” 的部分,和展示如何使用 `add_one` 函数的代码。可以运行 `cargo doc` 来生成这个文档注释的 HTML 文档。这个命令运行由 Rust 分发的工具 `rustdoc` 并将生成的 HTML 文档放入 *target/doc* 目录
在文档注释中增加示例代码块是一个清楚的表明如何使用库的方法。这么做还有一个额外的好处:`cargo test`也会像测试那样运行文档中的示例代码!没有什么比有例子的文档更好的了!也没有什么比不能正常工作的例子更糟的了,因为代码在编写文档时已经改变。尝试`cargo test`运行列表 14-1 中`add_one`函数的文档;将会看到如下测试结果 为了方便起见,运行 `cargo doc --open` 会构建当前 crate 文档(同时还有所有 ceate 依赖的文档)的 HTML 并在浏览器中打开。导航到 `add_one` 函数将会发现文档注释的文本是如何渲染的,如图 13-3 所示
```test <img alt="`my_crate` 的 `add_one` 函数所渲染的文档注释 HTML" src="img/trpl14-03.png" class="center" />
Doc-tests add-one
<span class="caption">图 14-3`add_one` 函数的文档注释 HTML</span>
#### 常用(文档注释)部分
列表 14-2 中使用了 `# Examples` Markdown 标题在 HTML 中创建了一个以 “Examples” 为标题的部分。一些其他经常在文档注释中使用的部分有:
- Panics这个函数可能会 `panic!` 的场景。并不希望程序崩溃的函数调用者应该确保他们不会在这些情况下调用此函数。
- Errors如果这个函数返回 `Result`,此部分描述可能会出现何种错误以及什么情况会造成这些错误,这有助于调用者编写代码来采用不同的方式处理不同的错误。
- Safety如果这个函数使用 `unsafe` 代码(这会在第十九章讨论),这一部分应该会涉及到期望函数调用者支持的确保 `unsafe` 块中代码正常工作的不变条件invariants
大部分文档注释不需要所有这些部分,不过这是一个提醒你检查调用你代码的人有兴趣了解的内容的列表。
#### 文档注释作为测试
在文档注释中增加示例代码块是一个清楚的表明如何使用库的方法,这么做还有一个额外的好处:`cargo test` 也会像测试那样运行文档中的示例代码!没有什么比有例子的文档更好的了!也没有什么比不能正常工作的例子更糟的了,因为代码在编写文档时已经改变。尝试 `cargo test` 运行像列表 14-2 中 `add_one` 函数的文档;应该在测试结果中看到像这样的部分:
```text
Doc-tests my_crate
running 1 test running 1 test
test add_one_0 ... ok test src/lib.rs - add_one (line 5) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
``` ```
尝试改变示例或函数并观察`cargo test`会捕获不再能运行的例子! 现在尝试改变函数或例子来使例子中的 `assert_eq!` 产生 panic。再次运行 `cargo test`,你将会看到文档测试捕获到了例子与代码不再同步
还有另一种风格的文档注释,`//!`用于注释包含项的结构例如crate、模块或函数而不是其之后的项。这通常用在 crate 的根lib.rs或模块的根mod.rs来分别编写 crate 或模块整体的文档。如下是包含整个标准库的`libstd`模块的文档: #### 注释包含项的结构
``` 还有另一种风格的文档注释,`//!`,这为包含注释的项,而不是注释之后的项增加文档。这通常用于 crate 根文件或模块的根文件为 crate 或模块整体提供文档。
//! # The Rust Standard Library
作为一个例子,如果我们希望增加描述包含 `add_one` 函数的 `my_crate` crate 目的的文档,可以在 *src/lib.rs* 开头增加以 `//!` 开头的注释,如列表 14-4 所示:
<span class="filename">文件名: src/lib.rs</span>
```rust,ignore
//! # My Crate
//! //!
//! The Rust Standard Library provides the essential runtime //! `my_crate` is a collection of utilities to make performing certain
//! functionality for building portable Rust software. //! calculations more convenient.
/// Adds one to the number given.
// ...snip...
``` ```
### 使用`pub use`来导出合适的公有 API <span class="caption">列表 14-4`my_crate` crate 整体的文档</span>
注意 `//!` 的最后一行之后没有任何代码。因为他们以 `//!` 开头而不是 `///`,这是属于包含此注释的项而不是注释之后项的文档。在这个情况中,包含这个注释的项是 *src/lib.rs* 文件,也就是 crate 根文件。这些注释描述了整个 crate。
如果运行 `cargo doc --open`,将会发现这些注释显示在 `my_crate` 文档的首页,位于 crate 中公有项列表之上,如图 14-5 所示:
<img alt="crate 整体注释所渲染的 HTML 文档" src="img/trpl14-05.png" class="center" />
<span class="caption">图 14-5包含 `my_crate` 整体描述的注释所渲染的文档</span>
位于项之中的文档注释对于描述 crate 和模块特别有用。使用他们描述其容器整体的目的来帮助 crate 用户理解你的代码组织。
第七章介绍了如何使用`mod`关键字来将代码组织进模块中,如何使用`pub`关键字将项变为公有,和如何使用`use`关键字将项引入作用域。当发布 crate 给并不熟悉其使用的库的实现的人时,就值得花时间考虑 crate 的结构对于开发和对于依赖 crate 的人来说是否同样有用。如果结构对于供其他库使用来说并不方便,也无需重新安排内部组织:可以选择使用`pub use`来重新导出一个不同的公有结构。 ### 使用 `pub use` 导出合适的公有 API
例如列表 14-2 中,我们创建了一个库`art`,其包含一个`kinds`模块,模块中包含枚举`Color`和包含函数`mix`的模块`utils` 第七章介绍了如何使用 `mod` 关键字来将代码组织进模块中,如何使用 `pub` 关键字将项变为公有,和如何使用 `use` 关键字将项引入作用域。然而对你开发来说很有道理的结果可能对用户来说就不太方便了。你可能希望将结构组织进有多个层次的层级中,不过想要使用被定义在很深层级中的类型的人可能很难发现这些类型是否存在。他们也可能会厌烦 `use my_crate::some_module::another_module::UsefulType;` 而不是 `use my_crate::UsefulType;` 来使用类型。
<span class="filename">Filename: src/lib.rs</span> 公有 API 的结构是你发布 crate 时主要需要考虑的。crate 用户没有你那么熟悉其结构,并且如何模块层级过大他们可能会难以找到所需的部分。
好消息是,如果结果对于用户来说 **不是** 很方便,你也无需重新安排内部组织:你可以选择使用 `pub use` 重导出re-export项来使公有结构不同于私有结构。重导出获取位于一个位置的公有项并将其公开到另一个位置好像它就定义在这个新位置一样。
例如,假设我们创建了一个模块化了充满艺术化气息的库 `art`。在这个库中是一个包含两个枚举 `PrimaryColor``SecondaryColor` 的模块 `kinds`,以及一个包含函数 `mix` 的模块 `utils`,如列表 14-6 所示:
<span class="filename">文件名: src/lib.rs</span>
```rust,ignore ```rust,ignore
//! # Art //! # Art
@ -94,17 +138,24 @@ pub mod utils {
/// a secondary color. /// a secondary color.
pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor { pub fn mix(c1: PrimaryColor, c2: PrimaryColor) -> SecondaryColor {
// ...snip... // ...snip...
# SecondaryColor::Green # SecondaryColor::Green
} }
} }
``` ```
<span class="caption">Listing 14-2: An `art` library with items organized into <span class="caption">列表 14-6一个库 `art` 其组织包含 `kinds``utils` 模块</span>
`kinds` and `utils` modules</span>
`cargo doc` 所生成的 crate 文档首页如图 14-7 所示:
<img alt="包含 `kinds``utils` 模块的 `art`" src="img/trpl14-07.png" class="center" />
<span class="caption">图 14-7包含 `kinds``utils` 模块的库 `art` 的文档首页</span>
为了使用这个库,列表 14-3 中另一个 crate 中使用了`use`语句: 注意 `PrimaryColor``SecondaryColor` 类型没有在首页中列出,`mix` 函数也是。必须点击 `kinds``utils` 才能看到他们。
<span class="filename">Filename: src/main.rs</span> 另一个依赖这个库的 crate 需要 `use` 语句来导入 `art` 中的项,这包含指定其当前定义的模块结构。列表 14-8 展示了一个使用 `art` crate 中 `PrimaryColor``mix` 项的 crate 的例子:
<span class="filename">文件名: src/main.rs</span>
```rust,ignore ```rust,ignore
extern crate art; extern crate art;
@ -119,14 +170,13 @@ fn main() {
} }
``` ```
<span class="caption">Listing 14-3: A program using the `art` crate's items <span class="caption">列表 14-8一个通过导出内部结构使用 `art` crate 中项的 crate</span>
with its internal structure exported</span>
库的用户并不需要知道`PrimaryColor`和`SecondaryColor`位于`kinds`模块中和`mix`位于`utils`模块中;这些结构对于内部组织是有帮助的,不过对于外部的观点来说没有什么意义 列表 14-8 中使用 `art` crate 代码的作者不得不搞清楚 `PrimaryColor` 位于 `kinds` 模块而 `mix` 位于 `utils` 模块。`art` crate 的模块结构相比使用它的开发者来说对编写它的开发者更有意义。其内部的 `kinds` 模块和 `utils` 模块的组织结构并没有对尝试理解如何使用它的人提供任何有价值的信息。`art` crate 的模块结构因不得不搞清楚所需的内容在何处和必须在 `use` 语句中指定模块名称而显得混乱和不便
此,可以选择在列表 14-2 中增加如下`pub use`语句来将这些类型重新导出到顶级结构,如列表 14-4 所示: 了从公有 API 中去掉 crate 的内部组织,我们可以采用列表 14-6 中的 `art` crate 并增加 `pub use` 语句来重导出项到顶层结构,如列表 14-9 所示:
<span class="filename">Filename: src/lib.rs</span> <span class="filename">文件名: src/lib.rs</span>
```rust,ignore ```rust,ignore
//! # Art //! # Art
@ -139,16 +189,24 @@ pub use utils::mix;
pub mod kinds { pub mod kinds {
// ...snip... // ...snip...
}
pub mod utils {
// ...snip...
}
``` ```
<span class="caption">Listing 14-4: Adding `pub use` statements to re-export <span class="caption">列表 14-9增加 `pub use` 语句重导出项</span>
items</span>
现在此 crate 由 `cargo doc` 生成的 API 文档会在首页列出重导出的项以及其链接,如图 14-10 所示,这就使得这些类型易于查找。
<!-- Will add ghosting in libreoffice /Carol --> <img alt="Rendered documentation for the `art` crate with the re-exports on the front page" src="img/trpl14-10.png" class="center" />
重导出的项将会被连接和排列在 crate API 文档的头版。`art` crate 的用户仍然可以像列表 14-3 那样使用内部结构,或者使用列表 14-4 中更方便的结构,如列表 14-5 所示: <span class="caption">图 14-10`art` 文档的首页,这里列出了重导出的项</span>
<span class="filename">Filename: src/main.rs</span> `art` crate 的用户仍然可以看见和选择使用列表 14-8 中的内部结构,或者可以使用列表 14-9 中更为方便的结构,如列表 14-11 所示:
<span class="filename">文件名: src/main.rs</span>
```rust,ignore ```rust,ignore
extern crate art; extern crate art;
@ -161,50 +219,59 @@ fn main() {
} }
``` ```
<span class="caption">Listing 14-5: Using the re-exported items from the `art` <span class="caption">列表 14-11一个使用 `art` crate</span>
crate</span>
<!-- Will add ghosting in libreoffice /Carol -->
创建一个有用的公有 API 结构更像一种艺术而不是科学。选择`pub use`提供了如何向用户暴露 crate 内部结构的灵活性。观察一些你所安装的 crate 的代码来看看其内部结构是否不同于公有 API 对于有很多嵌套模块的情况,使用 `pub use` 将类型重导出到顶级结构对于使用 crate 的人来说将会是大为不同的体验。
### 在第一次发布之前 创建一个有用的公有 API 结构更像是一门艺术而非科学,你可以反复检视他们来找出最适合用户的 API。选择 `pub use` 提供了组织 crate 内部结构和与终端用户体现解耦的灵活性。观察一些你所安装的 crate 的代码来看看其内部结构是否不同于公有 API。
在能够发布任何 crate 之前,你需要在[crates.io]上注册一个账号并获取一个 API token。为此[访问其官网][crates.io]并使用 GitHub 账号登陆。目前 GitHub 账号是必须的,不过将来网站可能会支持其他创建账号的方法。一旦登陆之后,查看[Account Settings]页面并使用其中指定的 API key 运行`cargo login`命令,这看起来像这样: ### 创建 Crates.io 账号
[crates.io]: https://crates.io 在你可以发布任何 crate 之前,需要在 crates.io 上注册账号并获取一个 API token。为此访问位于 *https://crates.io* 的官网并使用 GitHub 账号登陆————目前 GitHub 账号是必须的,不过将来该网站可能会支持其他创建账号的方法。一旦登陆之后,查看位于 *https://crates.io/me* 的账户设置页面并获取 API token。接着使用该 API token 运行 `cargo login` 命令,像这样:
[Account Settings]: https://crates.io/me
``` ```text
$ cargo login abcdefghijklmnopqrstuvwxyz012345 $ cargo login abcdefghijklmnopqrstuvwxyz012345
``` ```
这个命令会通知 Cargo 你的 API token 并将其储存在本地的 *~/.cargo/config* 文件中。注意这个 token 是一个**秘密****secret**)并不应该与其他人共享。如果因为任何原因与他人共享了这个信息,应该立即重新生成这个 token。 这个命令会通知 Cargo 你的 API token 并将其储存在本地的 *~/.cargo/config* 文件中。注意这个 token 是一个 **秘密****secret**)且不应该与其他人共享。如果因为任何原因与他人共享了这个信息,应该立即重新生成这个 token。
### 发布新 crate 之前 ### 发布新 crate 之前
首先crate 必须有一个位移的名称。虽然在本地开发 crate 时,可以使用任何你喜欢的名字,不过[crates.io]上的 crate 名称遵守先到先得的原则分配。一旦一个 crate 名被使用,就不能被另一个 crate 所使用,所以请确认你喜欢的名字在网站上是可用的 有了账号之后,比如说你已经有一个希望发布的 crate。在发布之前你需要在 crate 的 *Cargo.toml* 文件的 `[package]` 部分增加一些本 crate 的元信息metadata
如果尝试发布由`cargo new`生成的 crate会出现一个警告接着是一个错误 首先 crate 需要一个唯一的名称。虽然在本地开发 crate 时,可以使用任何你喜欢的名称。不过 Crates.io 上的 crate 名称遵守先到先得的分配原则。一旦某个 crate 名称被使用,其他人就不能再发布这个名称的 crate 了。请在网站上搜索你希望使用的名称来找出它是否已被使用。如果没有,修改 *Cargo.toml*`[package]` 里的名称为你希望用于发布的名称,像这样
```toml
[package]
name = "guessing_game"
``` ```
即使你选择了一个唯一的名称,如果此时尝试运行 `cargo publish` 发布该 crate 的话,会得到一个一个警告接着是一个错误:
```text
$ cargo publish $ cargo publish
Updating registry `https://github.com/rust-lang/crates.io-index` Updating registry `https://github.com/rust-lang/crates.io-index`
warning: manifest has no description, license, license-file, documentation, warning: manifest has no description, license, license-file, documentation,
homepage or repository. homepage or repository.
...snip... ...snip...
error: api errors: missing or empty metadata fields: description, license. error: api errors: missing or empty metadata fields: description, license.
Please see http://doc.crates.io/manifest.html#package-metadata for how to
upload metadata
``` ```
我们可以在包的 *Cargo.toml* 文件中包含更多的信息。其中一些字段是可选的,不过描述和 license 是发布所必须的,因为这样人们才能知道 crate 是干什么的已经在什么样的条款下可以使用他们。 这是因为我们缺少一些关键信息:关于该 crate 用途的描述和用户可能在何种条款下使用该 crate 的 license。为了修正这个错误需要在 *Cargo.toml* 中引入这些信息。
描述通常是一两句话,因为它会出现在 crate 的搜索结果中和 crate 页面里。对于 `license` 字段,你需要一个 **license 标识符值 ***license identifier value*。Linux 基金会位于 *http://spdx.org/licenses/* 的 Software Package Data Exchange (SPDX) 列出了可以使用的标识符。例如,为了指定 crate 使用 MIT License增加 `MIT` 标识符:
```toml
[package]
name = "guessing_game"
license = "MIT"
```
描述连同 crate 一起出现在搜索结果和 crate 页面中。描述通常是一两句话。`license`字段获取一个 license 标识符值,其可能的值由 Linux 基金会的[Software Package Data Exchange (SPDX)][spdx]指定。如果你想要使用一个不存在于SPDX的 license则不使用`license`值,使用`license-file`来指定项目中包含你想要使用的 license 的文本的文件名。 如果你希望使用不存在于 SPDX 的 license则需要将 license 文本放入一个文件,将该文件包含进项目中,接着使用 `license-file` 来指定文件名而不是使用 `license` 字段
关于项目所适用的 license 的指导超出了本书的范畴。很多 Rust 社区成员选择与 Rust 自身相同的 license它是一个双许可的`MIT/Apache-2.0`,这表明可以通过斜杠来分隔指定多个 license。所以一个准备好发布的项目的 *Cargo.toml* 文件看起来像这样: 关于项目所适用的 license 指导超出了本书的范畴。很多 Rust 社区成员选择与 Rust 自身相同的 license这是一个双许可的 `MIT/Apache-2.0`————这展示了也可以通过斜杠来分隔来指定多个 license 标识符。
[spdx]: http://spdx.org/licenses/ 那么,有了唯一的名称、版本号、由 `cargo new` 新建项目时增加的作者信息、描述和所选择的 license已经准备好发布的项目的 *Cargo.toml* 文件可能看起来像这样:
```toml ```toml
[package] [package]
@ -217,53 +284,52 @@ license = "MIT/Apache-2.0"
[dependencies] [dependencies]
``` ```
请查看[crates.io 的文档][other-metadata]中关于其他可以指定元数据的内容,他们可以帮助你的 crate 更容易被发现和使用! [Cargo 的文档](http://doc.rust-lang.org/cargo/) 描述了其他可以指定的元信息,他们可以帮助你的 crate 更容易被发现和使用!
[other-metadata]: http://doc.crates.io/manifest.html#package-metadata
### 发布到 Crates.io ### 发布到 Crates.io
现在我们创建了一个账号,保存了 API token为 crate 选择了一个名字,并指定了所需的元数据,我们已经准备好发布了!发布 crate 是一个特定版本的 crate 被上传并托管在 crates.io 的过程 现在我们创建了一个账号,保存了 API token为 crate 选择了一个名字,并指定了所需的元数据,你已经准备好发布了!发布 crate 会上传特定版本的 crate 到 crates.io 以供他人使用
发布 crate 请多加小心,因为发布是**永久性的**。对应版本不能被覆盖,其代码也不可能被删除。然而,可以被发布的版本号却没有限制。 发布 crate 请多加小心,因为发布是 **永久性的***permanent*。对应版本不能被覆盖,其代码也不可能被删除。Crates.io 的一个主要目标是作为一个代码的永久文档服务器,这样所有依赖 Crates.io 中 crate 的项目都能一直正常工作。允许删除版本将不可能满足这个目标。然而,可以被发布的版本号却没有限制。
让我们运行`cargo publish`命令,这次它应该会成功因为已经指定了必须的元数据 让我们再次运行`cargo publish`命令。这次它应该会成功
``` ```text
$ cargo publish $ cargo publish
Updating registry `https://github.com/rust-lang/crates.io-index` Updating registry `https://github.com/rust-lang/crates.io-index`
Packaging guessing_game v0.1.0 (file:///projects/guessing_game) Packaging guessing_game v0.1.0 (file:///projects/guessing_game)
Verifying guessing_game v0.1.0 (file:///projects/guessing_game) Verifying guessing_game v0.1.0 (file:///projects/guessing_game)
Compiling guessing_game v0.1.0 Compiling guessing_game v0.1.0
(file:///projects/guessing_game/target/package/guessing_game-0.1.0) (file:///projects/guessing_game/target/package/guessing_game-0.1.0)
Finished debug [unoptimized + debuginfo] target(s) in 0.19 secs Finished dev [unoptimized + debuginfo] target(s) in 0.19 secs
Uploading guessing_game v0.1.0 (file:///projects/guessing_game) Uploading guessing_game v0.1.0 (file:///projects/guessing_game)
``` ```
恭喜!你现在向 Rust 社区分享了代码,而且任何人都可以轻松的将你的 crate 加入他们项目的依赖。 恭喜!你现在向 Rust 社区分享了代码,而且任何人都可以轻松的将你的 crate 加入他们项目的依赖。
### 发布已有 crate 的新版本 ### 发布现存 crate 的新版本
当你修改了 crate 并准备好发布新版本时,改变 *Cargo.toml* 中`version`所指定的值。请使用[语义化版本规则][semver]来根据修改的类型决定下一个版本呢号。接着运行`cargo publish`来上传新版本。 当你修改了 crate 并准备好发布新版本时,改变 *Cargo.toml* `version` 所指定的值。请使用 [语义化版本规则][semver] 来根据修改的类型决定下一个版本呢号。接着运行 `cargo publish` 来上传新版本。
[semver]: http://semver.org/ [semver]: http://semver.org/
### 使用`cargo yank`从 Crates.io 删除版本 ### 使用 `cargo yank` 从 Crates.io 撤回版本
发布版本时可能会出现意外因为这样那样的原因导致功能被破坏比如语法错误或忘记引入某些文件。对于这种情况Cargo 支持 *yanking*个版本。 虽然你不能删除之前版本的 crate但是可以阻止任何将来的项目将他们加入到依赖中。这在某个版本因为这样或那样的原因被破坏的情况很有用。对于这种情况Cargo 支持 **撤回***yanking*)某个版本。
标记一个版本的 crate 为 yank 意味着没有项目能够再开始依赖这个版本不过现存的已经依赖这个版本的项目仍然能够下载和依赖这个版本的内容。crates.io 的一个主要目的是作为一个代码的永久档案库这样能够保证所有的项目都能继续构建而允许删除一个版本违反了这个目标。本质上来说yank 意味着所有带有 *Cargo.lock* 的项目并不会被破坏,同时任何未来生成的 *Cargo.lock* 将不能使用被撤回的版本。 撤回某个版本会阻止新项目开始依赖此版本,不过所有现存此依赖的项目仍然能够下载和依赖这个版本。从本质上说,撤回意味着所有带有 *Cargo.lock* 的项目的依赖不会被破坏,同时任何新生成的 *Cargo.lock* 将不能使用被撤回的版本。
yank 并**不**意味着删除了任何代码。例如 yank 功能不打算删除意外上传的 secret。如果这发生了请立刻重置这些 secret。 为了撤回一个 crate运行 `cargo yank` 并指定希望撤回的版本:
为了 yank 一个版本的 crate运行`cargo yank`并指定需要 yank 的版本: ```text
```
$ cargo yank --vers 1.0.1 $ cargo yank --vers 1.0.1
``` ```
也可以撤销 yank并允许项目开始依赖这个版本通过在命令中加上`--undo` 也可以撤销撤回操作,并允许项目可以再次开始依赖某个版本,通过在命令上增加 `--undo`
```
```text
$ cargo yank --vers 1.0.1 --undo $ cargo yank --vers 1.0.1 --undo
``` ```
撤回 **并没有** 删除任何代码。举例来说,撤回功能并不意在删除不小心上传的秘密信息。如果出现了这种情况,请立即重新设置这些秘密信息。

@ -2,9 +2,12 @@
> [ch14-03-cargo-workspaces.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch14-03-cargo-workspaces.md) > [ch14-03-cargo-workspaces.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch14-03-cargo-workspaces.md)
> <br> > <br>
> commit d945f6d4046f4fc3c09326213100492790aebb45 > commit 6e53771a409794d9933c2a31310d78149b7e0534
第十二章中,我们构建一个包含二进制 crate 和库 crate 的包。不过如果库 crate 继续变得更大而我们想要进一步将包拆分为多个库 crate 呢随着包增长拆分出其主要组件将是非常有帮助的。对于这种情况Cargo 提供了一个叫**工作空间***workspaces*)的功能,它可以帮助我们管理多个相关的并行开发的包。 第十二章中,我们构建一个包含二进制 crate 和库 crate 的包。
不过如果库 crate 继续变得更大而我们想要进一步将包拆分为多个库 crate 呢随着包增长拆分出其主要组件将是非常有帮助的。对于这种情况Cargo 提供了一个叫**工作空间***workspaces*)的功能,它可以帮助我们管理多个相关的并行开发的包。
**工作空间**是一系列的包都共享同样的 *Cargo.lock* 和输出目录。让我们使用工作空间创建一个项目,这是我们熟悉的所以就可以关注工作空间的结构了。这里有一个二进制项目它使用了两个库:一个会提供`add_one`方法而第二个会提供`add_two`方法。让我们为这个二进制项目创建一个新 crate 作为开始: **工作空间**是一系列的包都共享同样的 *Cargo.lock* 和输出目录。让我们使用工作空间创建一个项目,这是我们熟悉的所以就可以关注工作空间的结构了。这里有一个二进制项目它使用了两个库:一个会提供`add_one`方法而第二个会提供`add_two`方法。让我们为这个二进制项目创建一个新 crate 作为开始:

Loading…
Cancel
Save