Skip to content

Commit

Permalink
Merge pull request #89 from yingang/main
Browse files Browse the repository at this point in the history
fix some cross references and formattings
  • Loading branch information
lispking authored Jan 29, 2025
2 parents f6a4640 + a55fbe1 commit 56248c8
Show file tree
Hide file tree
Showing 29 changed files with 170 additions and 165 deletions.
4 changes: 2 additions & 2 deletions src/afterword.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

<!-- 参考链接 -->

[第 33 条]: https://www.lurklurk.org/effective-rust/no-std.html
[第 34 条]: https://www.lurklurk.org/effective-rust/ffi.html
[第 33 条]: chapter_6/item33-no-std.md
[第 34 条]: chapter_6/item34-ffi.md

[在线文档]: https://rust-lang.github.io/async-book/
[异步 Rust]: https://learning.oreilly.com/library/view/async-rust/9781098149086/
Expand Down
10 changes: 5 additions & 5 deletions src/chapter_1/item1-use-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,11 +282,11 @@ struct DisplayProperties {

<!-- 参考链接 -->

[第 3 条]: ./item3-transform.md
[第 4 条]: ./item4-errors.md
[第 5 条]: ./item5-casts.md
[第 6 条]: ./item6-newtype.md
[第 7 条]: ./item7-builder.md
[第 3 条]: item3-transform.md
[第 4 条]: item4-errors.md
[第 5 条]: item5-casts.md
[第 6 条]: item6-newtype.md
[第 7 条]: item7-builder.md
[第 16 条]: ../chapter_3/item16-unsafe.md
[第 18 条]: ../chapter_3/item18-panic.md
[第 21 条]: ../chapter_4/item21-semver.md
Expand Down
26 changes: 13 additions & 13 deletions src/chapter_1/item2-use-types-2.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 第 2 条:使用类型系统表达常见行为

[第1条]讨论了如何在类型系统中表达数据结构;本节继续讨论在 Rust 的类型系统中行为的编码。
[第 1 条]讨论了如何在类型系统中表达数据结构;本节继续讨论在 Rust 的类型系统中行为的编码。

本条目所描述的机制通常会让人感觉熟悉,因为它们在其他语言中都有直接的类似物:

Expand All @@ -11,7 +11,7 @@
- 特征(Traits):描述适用于同一基础项的相关功能的集合。在许多其他语言中都有大致等效的概念,包括 C++ 中的抽象类以及 Go 和 Java 中的接口。

当然,所有这些机制都有Rust特定的细节,本条将会一一介绍。
在前面列出的内容中,特征(traits)对于本书来说意义最为重大,因为它们描述了 Rust 编译器和标准库提供的很多行为。[第2章] 重点讨论了关于设计和实现特征的相关内容,但它们的普遍性意味着它们在本章的其他条目中也会频繁出现。
在前面列出的内容中,特征(traits)对于本书来说意义最为重大,因为它们描述了 Rust 编译器和标准库提供的很多行为。[第 2 章] 重点讨论了关于设计和实现特征的相关内容,但它们的普遍性意味着它们在本章的其他条目中也会频繁出现。

## 函数和方法( Function and Methods )

Expand All @@ -35,7 +35,7 @@ fn show(x: f64) {

```

如果一个函数与特定的数据结构密切相关,它就表现为一个方法。方法通过 self 标识,对该类型实例进行操作,并包含在 impl DataStructure 块中。这以类似于其他语言的面向对象方式将相关数据和代码封装在一起;然而,在 Rust 中,方法不仅可以添加到`结构体`类型上,也可以添加到`枚举`类型上,这与 Rust 枚举的普遍性质相符([第1条])。
如果一个函数与特定的数据结构密切相关,它就表现为一个方法。方法通过 self 标识,对该类型实例进行操作,并包含在 impl DataStructure 块中。这以类似于其他语言的面向对象方式将相关数据和代码封装在一起;然而,在 Rust 中,方法不仅可以添加到`结构体`类型上,也可以添加到`枚举`类型上,这与 Rust 枚举的普遍性质相符([第 1 条])。

```rust
enum Shape {
Expand Down Expand Up @@ -124,7 +124,7 @@ help: you might have forgotten to call this function

相反,编译器错误表明该类型类似于 `fn(i32, i32) -> i32 {main::sum}`,一种完全内部于编译器的类型(即不能在用户代码中编写),它同时标识了特定的函数及其签名。

换句话说,`sum` 的类型既编码了函数的签名又编码了其位置(出于优化原因);这种类型可以自动强制转换为 `fn` 类型([第6条])。
换句话说,`sum` 的类型既编码了函数的签名又编码了其位置(出于优化原因);这种类型可以自动强制转换为 `fn` 类型([第 6 条])。
<!-- </div> -->

### 闭包
Expand Down Expand Up @@ -218,7 +218,7 @@ let z = add_n.internal_op(5);
assert_eq!(z, 8);
```

在这个概念性的上下文中持有的值通常是引用([第9条]),就像这里的例子,但它们也可以是环境中事物的可变引用,或者是通过在输入参数前使用 `move` 关键字而从环境中完全移出的值。
在这个概念性的上下文中持有的值通常是引用([第 9 条]),就像这里的例子,但它们也可以是环境中事物的可变引用,或者是通过在输入参数前使用 `move` 关键字而从环境中完全移出的值。

回到 `modify_all` 的例子,闭包不能用在期望函数指针的地方。

Expand Down Expand Up @@ -278,7 +278,7 @@ Rust 有三种不同的 `Fn*` 特征,它们之间表达了关于环境捕获

特征中的每个方法也有一个名称,这允许编译器区分具有相同签名的方法,更重要的是,它允许程序员推断方法的目的。

Rust 的特征大致类似于 Go 和 Java 中的“接口”,或者 C++ 中的“抽象类”(只有虚拟方法,没有数据成员)。特征的实现必须提供所有方法(但请注意特征定义可以包括默认的实现,[第13条]),并且还可以有相关联的数据,那些实现会使用这些数据。这意味着代码和数据在共同的抽象中以某种面向对象的方式一起封装。
Rust 的特征大致类似于 Go 和 Java 中的“接口”,或者 C++ 中的“抽象类”(只有虚拟方法,没有数据成员)。特征的实现必须提供所有方法(但请注意特征定义可以包括默认的实现,[第 13 条]),并且还可以有相关联的数据,那些实现会使用这些数据。这意味着代码和数据在共同的抽象中以某种面向对象的方式一起封装。

接受结构体并调用其方法的代码被限制只能与特定类型一起工作。如果有多个类型实现了公共行为,那么定义一个特征来封装这种行为,并让代码使用特征的方法而不是特定结构体的方法会更加灵活。

Expand All @@ -303,7 +303,7 @@ pub trait StableSort: Sort {}
一旦行为被封装到 Rust 的类型系统中作为一个特征,它可以以两种方式被使用:
- 作为特征约束(`trait bound`),它在编译时限制了哪些类型可以被一个泛型数据类型或方法所接受,或者
- 作为特征对象(`trait object`),它在运行时限制了哪些类型可以被存储或传递给一个方法。
[第12条] 更详细地讨论了这两种方式的权衡。
[第 12 条] 更详细地讨论了这两种方式的权衡。

## 特征约束

Expand Down Expand Up @@ -419,12 +419,12 @@ note: for a trait to be "object safe" it needs to allow building a vtable to all

<!-- 参考链接 -->

[第1条]: item1-use-types.md
[第2章]: /chapter_2//item10-std-traits.md
[第6条]: item6-newtype.md
[第9条]: item9-iterators.md
[第12条]: /chapter_2/item12-generics&trait-objects.md
[第13条]: /chapter_2/item13-use-default-impl.md
[第 1 条]: item1-use-types.md
[第 2 章]: ../chapter_2.md
[第 6 条]: item6-newtype.md
[第 9 条]: item9-iterators.md
[第 12 条]: ../chapter_2/item12-generics&trait-objects.md
[第 13 条]: ../chapter_2/item13-use-default-impl.md

[函数指针]: https://doc.rust-lang.org/std/primitive.fn.html
[鸭子类型]: https://en.wikipedia.org/wiki/Duck_typing
34 changes: 19 additions & 15 deletions src/chapter_1/item3-transform.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# 第 3 条:优先选择Option和Result转换,而非显式匹配表达式

[第1条] 阐述了枚举(`enum`)的优点,并展示了 `match` 表达式如何强制程序员考虑所有可能性;这个方法探讨了在某些情况下,你应尽量避免使用 `match` 表达式 —— 至少是显式地。
[第 1 条] 阐述了枚举(`enum`)的优点,并展示了 `match` 表达式如何强制程序员考虑所有可能性;这个方法探讨了在某些情况下,你应尽量避免使用 `match` 表达式 —— 至少是显式地。

[第1条] 还介绍了 Rust 标准库提供的两个无处不在的枚举:
[第 1 条] 还介绍了 Rust 标准库提供的两个无处不在的枚举:
- `Option<T>`,表示一个值(类型为 `T`)可能存在也可能不存在。
- `Result<T, E>`,用于当尝试返回一个值(类型为 `T`)的操作可能失败,并可能返回一个错误(类型为 `E`)。

Expand Down Expand Up @@ -30,8 +30,9 @@ if let Some(i) = &s.field {
}
```

然而,大多数时候,程序员通常的处理方式是提供相应的else分支:缺少值( `Option::None` ),或者返回一个可能会出现相关错误( `Result::Err(e)` )。设计能够应对失败路径的软件是困难的,而且其中大部分是本质的复杂性,无论多少语法支持都无法帮助——具体来说,就是决定如果一个操作失败了应该发生什么。
在某些情况下,正确的决定是采取“鸵鸟策略”——把我们的头埋进沙子里,明确地不去处理失败。你不能完全忽略错误分支,因为Rust要求代码必须处理`Error`枚举的两种变体,但你可以选择将失败视为致命的错误。在失败时执行一个`panic!`意味着程序会终止,但可以在成功的假设下来编写其余的代码。通过显式`match`来执行此操作会不必要地冗长:
然而,大多数时候,程序员通常的处理方式是提供相应的 `else` 分支:缺少值(`Option::None`),或者返回一个可能会出现的相关错误(`Result::Err(e)`)。设计能够应对失败路径的软件是困难的,而且其中大部分是本质的复杂性,无论多少语法支持都无法帮助——具体来说,就是决定如果一个操作失败了应该发生什么。

在某些情况下,正确的决定是采取“鸵鸟策略”——把我们的头埋进沙子里,明确地不去处理失败。你不能完全忽略错误分支,因为 Rust 要求代码必须处理 `Error` 枚举的两种变体,但你可以选择将失败视为致命的错误。在失败时执行一个 `panic!` 意味着程序会终止,但可以在成功的假设下来编写其余的代码。通过显式 `match` 来执行此操作会不必要地冗长:

```rust
let result = std::fs::File::open("/etc/passwd");
Expand All @@ -40,13 +41,16 @@ let f = match result {
Err(_e) => panic!("Failed to open /etc/passwd!"),
};
```
当值不存在时,`Option``Result`都提供了一对方法来提取它们的内部值和`panic!`,它们分别是 [unwrap](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap)[expect](https://doc.rust-lang.org/std/result/enum.Result.html#method.expect) 。后者允许个性化失败时的错误消息,但无论哪种情况,将错误处理被委托给`.unwrap()`后缀(但仍然存在)生成的代码都更短、更简单:

`Option``Result` 都提供了一对方法来提取它们的内部值并在值不存在时执行 `panic!`,它们分别是 [unwrap](https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap)[expect](https://doc.rust-lang.org/std/result/enum.Result.html#method.expect) 。后者允许个性化失败时的错误消息,并将错误处理委托给 `.unwrap()` 后缀,但无论哪种情况,生成的代码都更短、更简单:

```rust
let f = std::fs::File::open("/etc/passwd").unwrap();
```
但要明确的是:这些辅助函数仍然会引发 `panic!`,所以选择使用它们与选择直接 `panic!`[第18条])是一样的。

然而,在许多情况下,正确的错误处理是将决策推迟给其他人。这在编写库时尤其如此,因为库的代码可能会在库作者无法预见的各种不同环境中使用。为了使库更易用,优先使用 `Result` 而不是 `Option`来表示错误,即使这可能涉及不同错误类型之间的转换([第4条])。
但要明确的是:这些辅助函数仍然会引发 `panic!`,所以选择使用它们与选择直接 `panic!`[第 18 条])是一样的。

然而,在许多情况下,正确的错误处理是将决策推迟给其他人。这在编写库时尤其如此,因为库的代码可能会在库作者无法预见的各种不同环境中使用。为了使库更易用,优先使用 `Result` 而不是 `Option` 来表示错误,即使这可能涉及不同错误类型之间的转换([第 4 条])。

当然,这提出了一个问题:什么算作错误?在此示例中,无法打开文件肯定是一个错误,并且该错误的详细信息(没有此类文件?权限被拒绝?)可以帮助用户决定下一步要做什么。另一方面,由于切片为空而未能检索切片的first()元素并不是真正的错误,因此它在标准库中表示为Option返回类型。
在两种可能性之间进行选择需要判断,但如果错误可能传达任何有用的信息,则倾向于Result 。
Expand Down Expand Up @@ -86,15 +90,15 @@ pub fn find_user(username: &str) -> Result<UserId, std::io::Error> {
}
```

Rust 新手有时会对此感到困惑:问号运算符在一开始很难被注意到,导致人们怀疑这段代码怎么可能正常工作。然而,即使只有一个字符,类型系统仍然在起作用,确保覆盖了相关类型([第1条])表达的所有可能性——让程序员可以专注于主线代码路径,不受干扰。
Rust 新手有时会对此感到困惑:问号运算符在一开始很难被注意到,导致人们怀疑这段代码怎么可能正常工作。然而,即使只有一个字符,类型系统仍然在起作用,确保覆盖了相关类型([第 1 条])表达的所有可能性——让程序员可以专注于主线代码路径,不受干扰。

更重要的是,这些明显的方法调用通常没有额外的成本:它们都是标记为 `#[inline]` 的泛型函数,所以生成的代码通常会编译成与手动版本相同的机器代码。

这两个因素结合起来意味着你应该优先使用 `Option``Result` 转换,而不是显式的 `match` 表达式。

在之前的例子中,错误类型是一致的:内部和外部方法都使用 `std::io::Error` 表达错误。然而,情况往往并非如此;一个函数可能从各种不同的子库中累积错误,每个子库都使用不同的错误类型。

关于错误映射的讨论一般见[第4条];现在,只需知道一个手动映射:
关于错误映射的讨论一般见[第 4 条];现在,只需知道一个手动映射:

```rust
pub fn find_user(username: &str) -> Result<UserId, String> {
Expand All @@ -118,11 +122,11 @@ pub fn find_user(username: &str) -> Result<UserId, String> {
}
```

更好的是,甚至这可能也不必要 —— 如果外部错误类型可以通过实现标准特征 `From`[第5条])从内部错误类型创建,那么编译器将自动执行转换,无需调用 `.map_err()`
更好的是,甚至这可能也不必要 —— 如果外部错误类型可以通过实现标准特征 `From`[第 5 条])从内部错误类型创建,那么编译器将自动执行转换,无需调用 `.map_err()`

这类转换具有更广泛的通用性。问号运算符是一个强大的工具;使用 `Option``Result` 类型上的转换方法将它们调整到可以顺利处理的形态。

标准库提供了各种各样的转换方法来实现这一点,如下面的地图所示。根据[第18条],可能引发 `panic!` 的方法用红色突出显示。
标准库提供了各种各样的转换方法来实现这一点,如下面的地图所示。根据[第 18 条],可能引发 `panic!` 的方法用红色突出显示。

![转换方法](../images/transform.svg)

Expand Down Expand Up @@ -186,9 +190,9 @@ pub fn encrypted(&self) -> Vec<u8> {

<!-- 参考链接 -->

[第1条]: item1-use-types.md
[第4条]: item4-errors.md
[第5条]: item5-casts.md
[第18条]: https://www.lurklurk.org/effective-rust/panic.html
[第 1 条]: item1-use-types.md
[第 4 条]: item4-errors.md
[第 5 条]: item5-casts.md
[第 18 条]: ../chapter_3/item18-panic.md

[在线版本]: https://tinyurl.com/rust-transform
14 changes: 7 additions & 7 deletions src/chapter_1/item4-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,13 +333,13 @@ error[E0119]: conflicting implementations of trait `From<WrappedError>` for
[第 3 条]: item3-transform.md
[第 5 条]: item5-casts.md
[第 6 条]: item6-newtype.md
[第 10 条]: https://www.lurklurk.org/effective-rust/std-traits.html
[第 12 条]: /chapter_2/item12-generics&trait-objects.md
[第 13 条]: /chapter_2/item13-use-default-impl.md
[第 24 条]: https://www.lurklurk.org/effective-rust/re-export.html
[第 25 条]: https://www.lurklurk.org/effective-rust/dep-graph.html
[第 28 条]: https://www.lurklurk.org/effective-rust/macros.html
[第 33 条]: https://www.lurklurk.org/effective-rust/no-std.html
[第 10 条]: ../chapter_2/item10-std-traits.md
[第 12 条]: ../chapter_2/item12-generics&trait-objects.md
[第 13 条]: ../chapter_2/item13-use-default-impl.md
[第 24 条]: ../chapter_4/item24-re-export.md
[第 25 条]: ../chapter_4/item25-dep-graph.md
[第 28 条]: ../chapter_5/item28-use-macros-judiciously.md
[第 33 条]: ../chapter_6/item33-no-std.md

[anyhow]:https://docs.rs/anyhow
[thiserror]:https://docs.rs/thiserror
Expand Down
6 changes: 3 additions & 3 deletions src/chapter_1/item5-casts.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,6 @@ error[E0277]: the trait bound `u16: From<u32>` is not satisfied
[第 4 条]: item4-errors.md
[第 6 条]: item6-newtype.md
[第 8 条]: item8-references&pointer.md
[第 10 条]: https://www.lurklurk.org/effective-rust/std-traits.html
[第 14 条]: https://www.lurklurk.org/effective-rust/lifetimes.html
[第 29 条]: https://www.lurklurk.org/effective-rust/clippy.html
[第 10 条]: ../chapter_2/item10-std-traits.md
[第 14 条]: ../chapter_3/item14-lifetimes.md
[第 29 条]: ../chapter_5/item29-listen-to-clippy.md
2 changes: 1 addition & 1 deletion src/chapter_1/item6-newtype.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ impl fmt::Display for NewType {

[第 1 条]: item1-use-types.md
[第 5 条]: item5-casts.md
[第 25 条]: https://www.lurklurk.org/effective-rust/dep-graph.html
[第 25 条]: ../chapter_4/item25-dep-graph.md

[常见]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#using-the-newtype-pattern-to-implement-external-traits-on-external-types
[额外的语义]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction
Expand Down
6 changes: 3 additions & 3 deletions src/chapter_1/item7-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,9 @@ pub fn build(&self) -> Details {
<!-- 参考链接 -->
[第 10 条]:https://www.lurklurk.org/effective-rust/std-traits.html
[第 25 条]:https://www.lurklurk.org/effective-rust/dep-graph.html
[第 28 条]:https://www.lurklurk.org/effective-rust/macros.html
[第 10 条]: ../chapter_2/item10-std-traits.md
[第 25 条]: ../chapter_4/item25-dep-graph.md
[第 28 条]: ../chapter_5/item28-use-macros-judiciously.md
[Default]:https://doc.rust-lang.org/std/default/trait.Default.html
[derive_builder]:https://docs.rs/derive_builder/latest/derive_builder/
Loading

0 comments on commit 56248c8

Please sign in to comment.