mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 08:51:18 +08:00
Merge branch 'master' of https://github.com/bioinformatist/trpl-zh-cn
This commit is contained in:
commit
014bfe4c13
@ -50,7 +50,7 @@ error[E0282]: unable to infer enough type information about `_`
|
|||||||
|
|
||||||
另外,`isize` 和 `usize` 类型依赖运行程序的计算机架构:64 位架构上他们是 64 位的, 32 位架构上他们是 32 位的。
|
另外,`isize` 和 `usize` 类型依赖运行程序的计算机架构:64 位架构上他们是 64 位的, 32 位架构上他们是 32 位的。
|
||||||
|
|
||||||
可以使用表格 3-2 中的任何一种形式编写数字字面值。注意除字节以外的其它字面值允许使用类型后缀,例如 `57u8`,允许使用 `_` 做为分隔符以方便读数。
|
可以使用表格 3-2 中的任何一种形式编写数字字面值。注意除字节以外的其它字面值允许使用类型后缀,例如 `57u8`,同时也允许使用 `_` 做为分隔符以方便读数,例如`1_000`。
|
||||||
|
|
||||||
<span class="caption">表格 3-2: Rust 中的整型字面值</span>
|
<span class="caption">表格 3-2: Rust 中的整型字面值</span>
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ Rust 的核心功能(之一)是 **所有权**(*ownership*)。虽然这
|
|||||||
>
|
>
|
||||||
> 想象一下去餐馆就坐吃饭。当进入时,你说明有几个人,餐馆员工会找到一个够大的空桌子并领你们过去。如果有人来迟了,他们也可以通过询问来找到你们坐在哪。
|
> 想象一下去餐馆就坐吃饭。当进入时,你说明有几个人,餐馆员工会找到一个够大的空桌子并领你们过去。如果有人来迟了,他们也可以通过询问来找到你们坐在哪。
|
||||||
>
|
>
|
||||||
> 访问堆上的数据要比访问栈上的数据要慢因为必须通过指针来访问。现代的处理器在内存中跳转越少就越快(缓存)。继续类比,假设有一台服务器来处理来自多个桌子的订单。它在处理完一个桌子的所有订单后再移动到下一个桌子是最有效率的。从桌子 A 获取一个订单,接着再从桌子 B 获取一个订单,然后再从桌子 A,然后再从桌子 B 这样的流程会更加缓慢。出于同样原因,处理器在处理的数据之间彼此较近的时候(比如在栈上)比较远的时候(比如可能在堆上)能更好的工作。在堆上分配大量的空间也可能消耗时间。
|
> 访问堆上的数据要比访问栈上的数据要慢因为必须通过指针来访问。现代的处理器在内存中跳转越少就越快(缓存)。继续类比,假设有一个服务员在餐厅里处理多个桌子的点菜。在一个桌子报完所有菜后再移动到下一个桌子是最有效率的。从桌子 A 听一个菜,接着桌子 B 听一个菜,然后再桌子 A,然后再桌子 B 这样的流程会更加缓慢。出于同样原因,处理器在处理的数据之间彼此较近的时候(比如在栈上)比较远的时候(比如可能在堆上)能更好的工作。在堆上分配大量的空间也可能消耗时间。
|
||||||
>
|
>
|
||||||
> 当调用一个函数,传递给函数的值(包括可能指向堆上数据的指针)和函数的局部变量被压入栈中。当函数结束时,这些值被移出栈。
|
> 当调用一个函数,传递给函数的值(包括可能指向堆上数据的指针)和函数的局部变量被压入栈中。当函数结束时,这些值被移出栈。
|
||||||
>
|
>
|
||||||
|
@ -115,7 +115,7 @@ impl Summarizable for WeatherForecast {
|
|||||||
|
|
||||||
另外这段代码假设 `Summarizable` 是一个公有 trait,这是因为示例 10-12 中 `trait` 之前使用了 `pub` 关键字。
|
另外这段代码假设 `Summarizable` 是一个公有 trait,这是因为示例 10-12 中 `trait` 之前使用了 `pub` 关键字。
|
||||||
|
|
||||||
trait 实现的一个需要注意的限制是:只能在 trait 或对应类型位于我们 crate 本地的时候为其实现 trait。换句话说,不允许对外部类型实现外部 trait。例如,不能在 `Vec` 上实现 `Display` trait,因为 `Display` 和 `Vec` 都定义于标准库中。允许在像 `Tweet` 这样作为我们 `aggregator`crate 部分功能的自定义类型上实现标准库中的 trait `Display`。也允许在 `aggregator`crate 中为 `Vec` 实现 `Summarizable`,因为 `Summarizable` 定义与此。这个限制是我们称为 **孤儿规则**(*orphan rule*)的一部分,如果你感兴趣的可以在类型理论中找到它。简单来说,它被称为 orphan rule 是因为其父类型不存在。没有这条规则的话,两个 crate 可以分别对相同类型是实现相同的 trait,因而这两个实现会相互冲突:Rust 将无从得知应该使用哪一个。因为 Rust 强制执行 orphan rule,其他人编写的代码不会破坏你代码,反之亦是如此。
|
trait 实现的一个需要注意的限制是:只能在 trait 或对应类型位于我们 crate 本地的时候为其实现 trait。换句话说,不允许对外部类型实现外部 trait。例如,不能在 `Vec` 上实现 `Display` trait,因为 `Display` 和 `Vec` 都定义于标准库中。允许在像 `Tweet` 这样作为我们 `aggregator`crate 部分功能的自定义类型上实现标准库中的 trait `Display`。也允许在 `aggregator`crate 中为 `Vec` 实现 `Summarizable`,因为 `Summarizable` 定义于此。这个限制是我们称为 **孤儿规则**(*orphan rule*)的一部分,如果你感兴趣的可以在类型理论中找到它。简单来说,它被称为 orphan rule 是因为其父类型不存在。没有这条规则的话,两个 crate 可以分别对相同类型是实现相同的 trait,因而这两个实现会相互冲突:Rust 将无从得知应该使用哪一个。因为 Rust 强制执行 orphan rule,其他人编写的代码不会破坏你代码,反之亦是如此。
|
||||||
|
|
||||||
### 默认实现
|
### 默认实现
|
||||||
|
|
||||||
@ -311,7 +311,7 @@ fn main() {
|
|||||||
|
|
||||||
### 使用 trait bound 有条件的实现方法
|
### 使用 trait bound 有条件的实现方法
|
||||||
|
|
||||||
通过使用带有 trati bound 的泛型 `impl` 块,可以有条件的只为实现了特定 trait 的类型实现方法。例如,示例 10-17 中的类型 `Pair<T>` 总是实现了 `new` 方法,不过只有 `Pair<T>` 内部的 `T` 实现了 `PartialOrd` trait 来允许比较和 `Display` trait 来启用打印,才会实现 `cmp_display`:
|
通过使用带有 trait bound 的泛型 `impl` 块,可以有条件的只为实现了特定 trait 的类型实现方法。例如,示例 10-17 中的类型 `Pair<T>` 总是实现了 `new` 方法,不过只有 `Pair<T>` 内部的 `T` 实现了 `PartialOrd` trait 来允许比较和 `Display` trait 来启用打印,才会实现 `cmp_display`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
@ -361,4 +361,4 @@ blanket implementation 会出现在 trait 文档的 “Implementers” 部分。
|
|||||||
|
|
||||||
trait 和 trait bound 让我们使用泛型类型参数来减少重复,并仍然能够向编译器明确指定泛型类型需要拥有哪些行为。因为我们向编译器提供了 trait bound 信息,它就可以检查代码中所用到的具体类型是否提供了正确的行为。在动态类型语言中,如果我们尝试调用一个类型并没有实现的方法,会在运行时出现错误。Rust 将这些错误移动到了编译时,甚至在代码能够运行之前就强迫我们修复错误。另外,我们也无需编写运行时检查行为的代码,因为在编译时就已经检查过了,这样相比其他那些不愿放弃泛型灵活性的语言有更好的性能。
|
trait 和 trait bound 让我们使用泛型类型参数来减少重复,并仍然能够向编译器明确指定泛型类型需要拥有哪些行为。因为我们向编译器提供了 trait bound 信息,它就可以检查代码中所用到的具体类型是否提供了正确的行为。在动态类型语言中,如果我们尝试调用一个类型并没有实现的方法,会在运行时出现错误。Rust 将这些错误移动到了编译时,甚至在代码能够运行之前就强迫我们修复错误。另外,我们也无需编写运行时检查行为的代码,因为在编译时就已经检查过了,这样相比其他那些不愿放弃泛型灵活性的语言有更好的性能。
|
||||||
|
|
||||||
这里还有一种泛型,我们一直在使用它甚至都没有察觉它的存在,这就是 **生命周期**(*lifetimes*)。不同于其他泛型帮助我们确保类型拥有期望的行为,生命周期则有助于确保引用在我们需要他们的时候一直有效。让我们学习生命周期是如何做到这些的。
|
这里还有一种泛型,我们一直在使用它甚至都没有察觉它的存在,这就是 **生命周期**(*lifetimes*)。不同于其他泛型帮助我们确保类型拥有期望的行为,生命周期则有助于确保引用在我们需要他们的时候一直有效。让我们学习生命周期是如何做到这些的。
|
||||||
|
@ -176,7 +176,7 @@ error[E0106]: missing lifetime specifier
|
|||||||
|
|
||||||
生命周期注解有着一个不太常见的语法:生命周期参数名称必须以撇号(`'`)开头。生命周期参数的名称通常全是小写,而且类似于泛型类型,其名称通常非常短。`'a` 是大多数人默认使用的名称。生命周期参数注解位于引用的 `&` 之后,并有一个空格来将引用类型与生命周期注解分隔开。
|
生命周期注解有着一个不太常见的语法:生命周期参数名称必须以撇号(`'`)开头。生命周期参数的名称通常全是小写,而且类似于泛型类型,其名称通常非常短。`'a` 是大多数人默认使用的名称。生命周期参数注解位于引用的 `&` 之后,并有一个空格来将引用类型与生命周期注解分隔开。
|
||||||
|
|
||||||
这里有一些例子:我们有一个没有生命周期参数的 `i32` 的引用,一个有叫做 `'a` 的生命周期参数的 `i32` 的引用,和一个也有的生命周期参数 `'a` 的 `i32` 的可变引用:
|
这里有一些例子:我们有一个没有生命周期参数的 `i32` 的引用,一个有叫做 `'a` 的生命周期参数的 `i32` 的引用,和一个生命周期也是 `'a` 的 `i32` 的可变引用:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
&i32 // a reference
|
&i32 // a reference
|
||||||
@ -184,7 +184,7 @@ error[E0106]: missing lifetime specifier
|
|||||||
&'a mut i32 // a mutable reference with an explicit lifetime
|
&'a mut i32 // a mutable reference with an explicit lifetime
|
||||||
```
|
```
|
||||||
|
|
||||||
生命周期注解本身没有多少意义:生命周期注解告诉 Rust 多个引用的泛型生命周期参数如何相互联系。如果函数有一个生命周期 `'a` 的 `i32` 的引用的参数 `first`,还有另一个同样是生命周期 `'a` 的 `i32` 的引用的参数 `second`,这两个生命周期注解有相同的名称意味着 `first` 和 `second` 必须与这相同的泛型生命周期存在得一样久。
|
单个的生命周期注解本身没有多少意义:生命周期注解告诉 Rust 多个引用的泛型生命周期参数如何相互联系。如果函数有一个生命周期 `'a` 的 `i32` 的引用的参数 `first`,还有另一个同样是生命周期 `'a` 的 `i32` 的引用的参数 `second`,这两个生命周期注解有相同的名称意味着 `first` 和 `second` 必须与这相同的泛型生命周期存在得一样久。
|
||||||
|
|
||||||
### 函数签名中的生命周期注解
|
### 函数签名中的生命周期注解
|
||||||
|
|
||||||
@ -208,7 +208,7 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
|||||||
|
|
||||||
现在函数签名表明对于某些生命周期 `'a`,函数会获取两个参数,他们都是与生命周期 `'a` 存在的一样长的字符串 slice。函数会返回一个同样也与生命周期 `'a` 存在的一样长的字符串 slice。这就是我们告诉 Rust 需要其保证的协议。
|
现在函数签名表明对于某些生命周期 `'a`,函数会获取两个参数,他们都是与生命周期 `'a` 存在的一样长的字符串 slice。函数会返回一个同样也与生命周期 `'a` 存在的一样长的字符串 slice。这就是我们告诉 Rust 需要其保证的协议。
|
||||||
|
|
||||||
通过在函数签名中指定生命周期参数,不会改变任何参数或返回值的生命周期,不过我们说过任何不坚持这个协议的类型都将被借用检查器拒绝。这个函数并不知道(或需要知道)`x` 和 `y` 具体会存在多久,不过只需要知道一些可以使用 `'a` 替代的作用域将会满足这个签名。
|
我们并不会通过“在函数签名中指定生命周期参数”这种方式来改变任何参数或返回值的生命周期,而是指出任何不遵守这个协议的传入值都将被借用检查器拒绝。这个函数并不知道(或需要知道)`x` 和 `y` 具体会存在多久,而只需要知道有某个可以被 `'a` 替代的作用域将会满足这个签名。
|
||||||
|
|
||||||
当在函数中使用生命周期注解时,这些注解出现在函数签名中,而不存在于函数体中的任何代码中。这是因为 Rust 能够分析函数中代码而不需要任何协助,不过当函数引用或被函数之外的代码引用时,参数或返回值的生命周期可能在每次函数被调用时都不同。这可能会产生惊人的消耗并且对于 Rust 来说通常是不可能分析的。在这种情况下,我们需要自己标注生命周期。
|
当在函数中使用生命周期注解时,这些注解出现在函数签名中,而不存在于函数体中的任何代码中。这是因为 Rust 能够分析函数中代码而不需要任何协助,不过当函数引用或被函数之外的代码引用时,参数或返回值的生命周期可能在每次函数被调用时都不同。这可能会产生惊人的消耗并且对于 Rust 来说通常是不可能分析的。在这种情况下,我们需要自己标注生命周期。
|
||||||
|
|
||||||
@ -240,7 +240,7 @@ fn main() {
|
|||||||
|
|
||||||
<span class="caption">示例 10-24:通过拥有不同的具体生命周期的 `String` 值调用 `longest` 函数</span>
|
<span class="caption">示例 10-24:通过拥有不同的具体生命周期的 `String` 值调用 `longest` 函数</span>
|
||||||
|
|
||||||
接下来,让我们尝试一个 `result` 的引用的生命周期必须比两个参数的要短的例子。将 `result` 变量的声明从内部作用域中移动出来,不过将 `result` 和 `string2` 变量的赋值语句一同放在内部作用域里。接下来,我们将使用 `result` 的 `println!` 移动到内部作用域之外,就在其结束之后。注意示例 10-25 中的代码不能编译:
|
接下来,让我们尝试一个 `result` 的引用的生命周期肯定比两个参数的要短的例子。将 `result` 变量的声明从内部作用域中移动出来,但是将 `result` 和 `string2` 变量的赋值语句一同放在内部作用域里。接下来,我们将使用 `result` 的 `println!` 移动到内部作用域之外,就在其结束之后。注意示例 10-25 中的代码不能编译:
|
||||||
|
|
||||||
<span class="filename">文件名: src/main.rs</span>
|
<span class="filename">文件名: src/main.rs</span>
|
||||||
|
|
||||||
@ -521,4 +521,4 @@ fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a st
|
|||||||
|
|
||||||
这一章介绍了很多的内容!现在你知道了泛型类型参数、trait 和 trait bounds 以及泛型生命周期类型,你已经准备好编写既不重复又能适用于多种场景的代码了。泛型类型参数意味着代码可以适用于不同的类型。trait 和 trait bounds 保证了即使类型是泛型的,这些类型也会拥有所需要的行为。由生命周期注解所指定的引用生命周期之间的关系保证了这些灵活多变的代码不会出现悬垂引用。而所有的这一切发生在编译时所以不会影响运行时效率!
|
这一章介绍了很多的内容!现在你知道了泛型类型参数、trait 和 trait bounds 以及泛型生命周期类型,你已经准备好编写既不重复又能适用于多种场景的代码了。泛型类型参数意味着代码可以适用于不同的类型。trait 和 trait bounds 保证了即使类型是泛型的,这些类型也会拥有所需要的行为。由生命周期注解所指定的引用生命周期之间的关系保证了这些灵活多变的代码不会出现悬垂引用。而所有的这一切发生在编译时所以不会影响运行时效率!
|
||||||
|
|
||||||
你可能不会相信,这个领域还有更多需要学习的内容:第十七章会讨论 trait 对象,这是另一种使用 trait 的方式。第十九章会涉及到生命周期注解更复杂的场景。第二十章讲解一些高级的类型系统功能。不过接下来,让我们聊聊如何在 Rust 中编写测试,来确保代码的所有功能能像我们希望的那样工作!
|
你可能不会相信,这个领域还有更多需要学习的内容:第十七章会讨论 trait 对象,这是另一种使用 trait 的方式。第十九章会涉及到生命周期注解更复杂的场景。第二十章讲解一些高级的类型系统功能。不过接下来,让我们聊聊如何在 Rust 中编写测试,来确保代码的所有功能能像我们希望的那样工作!
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
> <br>
|
> <br>
|
||||||
> commit d06a6a181fd61704cbf7feb55bc61d518c6469f9
|
> commit d06a6a181fd61704cbf7feb55bc61d518c6469f9
|
||||||
|
|
||||||
Rust 的设计灵感来源于很多现存的语言和计数。其中一个显著的影响就是 **函数式编程**(*functional programming*)。函数式编程风格通常包含将函数作为参数值或其他函数的返回值、将函数赋值给变量以供之后执行等等。这里我们将不会辩论函数式编程到底是什么或不是什么,而是突出展示 Rust 中那些类似很多经常被认为是函数式的语言中功能的功能。
|
Rust 的设计灵感来源于很多现存的语言和技术。其中一个显著的影响就是 **函数式编程**(*functional programming*)。函数式编程风格通常包含将函数作为参数值或其他函数的返回值、将函数赋值给变量以供之后执行等等。这里我们将不会辩论函数式编程到底是什么或不是什么,而是突出展示 Rust 中那些类似很多经常被认为是函数式的语言中功能的功能。
|
||||||
|
|
||||||
更具体的,我们将要涉及:
|
更具体的,我们将要涉及:
|
||||||
|
|
||||||
@ -13,4 +13,4 @@ Rust 的设计灵感来源于很多现存的语言和计数。其中一个显著
|
|||||||
* 如何使用这些功能来改进第十二章的 I/O 项目
|
* 如何使用这些功能来改进第十二章的 I/O 项目
|
||||||
* 这些功能的性能。**剧透高能:** 他们的速度超乎你的想象!
|
* 这些功能的性能。**剧透高能:** 他们的速度超乎你的想象!
|
||||||
|
|
||||||
还有其他受函数式风格影响的 Rust 功能,比如模式匹配和枚举,这些已经在其他章节中讲到过了。掌握闭包和迭代器则是编写符合语言风格的高性能 Rust 代码的重要一环,所以我们将专门用一整章来讲解他们。
|
还有其他受函数式风格影响的 Rust 功能,比如模式匹配和枚举,这些已经在其他章节中讲到过了。掌握闭包和迭代器则是编写符合语言风格的高性能 Rust 代码的重要一环,所以我们将专门用一整章来讲解他们。
|
||||||
|
Loading…
Reference in New Issue
Block a user