Merge pull request #636 from Xuzheng77s/main

补丁#01
pull/638/head
KaiserY 2 years ago committed by GitHub
commit b77ad69961
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,7 +1,8 @@
# 写个猜数字游戏 # 写个猜数字游戏
> [ch02-00-guessing-game-tutorial.md](https://github.com/rust-lang/book/blob/main/src/ch02-00-guessing-game-tutorial.md) > [ch02-00-guessing-game-tutorial.md](https://github.com/rust-lang/book/blob/main/src/ch02-00-guessing-game-tutorial.md)
> commit 4a8924fd5bb38d46f0b0d74f4beea7ab695fa1b3 > <br>
> commit 6e2fe7c0f085989cc498cec139e717e2af172cb7
让我们一起动手完成一个项目,来快速上手 Rust本章将介绍 Rust 中一些常用概念,并通过真实的程序来展示如何运用它们。你将会学到 `let`、`match`、方法method、关联函数associated function、使用外部 crate 等知识!后续章节会深入探讨这些概念的细节。在这一章,我们将练习基础内容。 让我们一起动手完成一个项目,来快速上手 Rust本章将介绍 Rust 中一些常用概念,并通过真实的程序来展示如何运用它们。你将会学到 `let`、`match`、方法method、关联函数associated function、使用外部 crate 等知识!后续章节会深入探讨这些概念的细节。在这一章,我们将练习基础内容。

@ -2,11 +2,11 @@
> [ch04-01-what-is-ownership.md](https://github.com/rust-lang/book/blob/main/src/ch04-01-what-is-ownership.md) > [ch04-01-what-is-ownership.md](https://github.com/rust-lang/book/blob/main/src/ch04-01-what-is-ownership.md)
> <br> > <br>
> commit a5e0c5b2c5f9054be3b961aea2c7edfeea591de8 > commit 9c9a522555c05cae6717adfbb419af58ebd1cea0
Rust 的核心功能(之一)是 **所有权***ownership*)。虽然该功能很容易解释,但它对语言的其他部分有着深刻的影响。 Rust 的核心功能(之一)是 **所有权***ownership*)。虽然该功能很容易解释,但它对语言的其他部分有着深刻的影响。
所有程序都必须管理其运行时使用计算机内存的方式。一些语言中具有垃圾回收机制,在程序运行时不断地寻找不再使用的内存在另一些语言中程序员必须亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。如果违反了任何这些规则,程序都不能编译。在运行时,所有权系统的任何功能都不会减慢程序。 所有程序都必须管理其运行时使用计算机内存的方式。一些语言中具有垃圾回收机制,在程序运行时有规律地寻找不再使用的内存在另一些语言中程序员必须亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列的规则进行检查。如果违反了任何这些规则,程序都不能编译。在运行时,所有权系统的任何功能都不会减慢程序。
因为所有权对很多程序员来说都是一个新概念,需要一些时间来适应。好消息是随着你对 Rust 和所有权系统的规则越来越有经验,你就越能自然地编写出安全和高效的代码。持之以恒! 因为所有权对很多程序员来说都是一个新概念,需要一些时间来适应。好消息是随着你对 Rust 和所有权系统的规则越来越有经验,你就越能自然地编写出安全和高效的代码。持之以恒!
@ -17,11 +17,11 @@ Rust 的核心功能(之一)是 **所有权***ownership*)。虽然该
> 在很多语言中,你并不需要经常考虑到栈与堆。不过在像 Rust 这样的系统编程语言中,值是位于栈上还是堆上在更大程度上影响了语言的行为以及为何必须做出这样的抉择。我们会在本章的稍后部分描述所有权与栈和堆相关的内容,所以这里只是一个用来预热的简要解释。 > 在很多语言中,你并不需要经常考虑到栈与堆。不过在像 Rust 这样的系统编程语言中,值是位于栈上还是堆上在更大程度上影响了语言的行为以及为何必须做出这样的抉择。我们会在本章的稍后部分描述所有权与栈和堆相关的内容,所以这里只是一个用来预热的简要解释。
> >
> 栈和堆都是代码在运行时可供使用的内存,但是它们的结构不同。栈以放入值的顺序存储值并以相反顺序取出值。这也被称作 **后进先出***last in, first out*)。想象一下一叠盘子:当增加更多盘子时,把它们放在盘子堆的顶部,当需要盘子时,也从顶部拿走。不能从中间也不能从底部增加或拿走盘子!增加数据叫做 **进栈***pushing onto the stack*),而移出数据叫做 **出栈***popping off the stack*)。栈中的所有数据都必须占用已知且固定的大小。在编译时大小未知或大小可能变化的数据,要改为存储在堆上。 > 栈和堆都是代码在运行时可供使用的内存,但是它们的结构不同。栈以放入值的顺序存储值并以相反顺序取出值。这也被称作 **后进先出***last in, first out*)。想象一下一叠盘子:当增加更多盘子时,把它们放在盘子堆的顶部,当需要盘子时,也从顶部拿走。不能从中间也不能从底部增加或拿走盘子!增加数据叫做 **进栈***pushing onto the stack*),而移出数据叫做 **出栈***popping off the stack*)。栈中的所有数据都必须占用已知且固定的大小。在编译时大小未知或大小可能变化的数据,要改为存储在堆上。
> 堆是缺乏组织的当向堆放入数据时你要请求一定大小的空间。内存分配器memory allocator在堆的某处找到一块足够大的空位把它标记为已使用并返回一个表示该位置地址的 **指针***pointer*)。这个过程称作 **在堆上分配内存***allocating on the heap*),有时简称为 “分配”allocating。将数据推入栈中并不被认为是分配。因为指向放入堆中数据的指针是已知的并且大小是固定的你可以将该指针存储在栈上不过当需要实际数据时必须访问指针。想象一下去餐馆就座吃饭。当进入时你说明有几个人餐馆员工会找到一个够大的空桌子并领你们过去。如果有人来迟了他们也可以通过询问来找到你们坐在哪。 > 堆是缺乏组织的当向堆放入数据时你要请求一定大小的空间。内存分配器memory allocator在堆的某处找到一块足够大的空位把它标记为已使用并返回一个表示该位置地址的 **指针***pointer*)。这个过程称作 **在堆上分配内存***allocating on the heap*),有时简称为 “分配”allocating将数据推入栈中并不被认为是分配。因为指向放入堆中数据的指针是已知的并且大小是固定的,你可以将该指针存储在栈上,不过当需要实际数据时,必须访问指针。想象一下去餐馆就座吃饭。当进入时,你说明有几个人,餐馆员工会找到一个够大的空桌子并领你们过去。如果有人来迟了,他们也可以通过询问来找到你们坐在哪。
> >
> 入栈比在堆上分配内存要快,因为(入栈时)分配器无需为存储新数据去搜索内存空间;其位置总是在栈顶。相比之下,在堆上分配内存则需要更多的工作,这是因为分配器必须首先找到一块足够存放数据的内存空间,并接着做一些记录为下一次分配做准备。 > 入栈比在堆上分配内存要快,因为(入栈时)分配器无需为存储新数据去搜索内存空间;其位置总是在栈顶。相比之下,在堆上分配内存则需要更多的工作,这是因为分配器必须首先找到一块足够存放数据的内存空间,并接着做一些记录为下一次分配做准备。
> >
> 访问堆上的数据比访问栈上的数据慢,因为必须通过指针来访问。现代处理器在内存中跳转越少就越快(缓存)。继续类比,假设有一个服务员在餐厅里处理多个桌子的点菜。在一个桌子报完所有菜后再移动到下一个桌子是最有效率的。从桌子 A 听一个菜,接着桌子 B 听一个菜,然后再桌子 A然后再桌子 B 这样的流程会更加缓慢。出于同样原因,处理器在处理的数据彼此较近的时候(比如在栈上)比较远的时候(比如可能在堆上)能更好的工作。在堆上分配大量的空间也可能消耗时间。 > 访问堆上的数据比访问栈上的数据慢,因为必须通过指针来访问。现代处理器在内存中跳转越少就越快(缓存)。继续类比,假设有一个服务员在餐厅里处理多个桌子的点菜。在一个桌子报完所有菜后再移动到下一个桌子是最有效率的。从桌子 A 听一个菜,接着桌子 B 听一个菜,然后再桌子 A然后再桌子 B 这样的流程会更加缓慢。出于同样原因,处理器在处理的数据彼此较近的时候(比如在栈上)比较远的时候(比如可能在堆上)能更好的工作。
> >
> 当你的代码调用一个函数时,传递给函数的值(包括可能指向堆上数据的指针)和函数的局部变量被压入栈中。当函数结束时,这些值被移出栈。 > 当你的代码调用一个函数时,传递给函数的值(包括可能指向堆上数据的指针)和函数的局部变量被压入栈中。当函数结束时,这些值被移出栈。
> >
@ -31,7 +31,7 @@ Rust 的核心功能(之一)是 **所有权***ownership*)。虽然该
首先,让我们看一下所有权的规则。当我们通过举例说明时,请谨记这些规则: 首先,让我们看一下所有权的规则。当我们通过举例说明时,请谨记这些规则:
> 1. Rust 中的每一个值都有一个被称为其 **所有者***owner*的变量 > 1. Rust 中的每一个值都有一个 **所有者***owner*)。
> 2. 值在任一时刻有且只有一个所有者。 > 2. 值在任一时刻有且只有一个所有者。
> 3. 当所有者(变量)离开作用域,这个值将被丢弃。 > 3. 当所有者(变量)离开作用域,这个值将被丢弃。
@ -197,7 +197,9 @@ Rust 采取了一个不同的策略:内存在拥有它的变量离开作用域
原因是像整型这样的在编译时已知大小的类型被整个存储在栈上,所以拷贝其实际的值是快速的。这意味着没有理由在创建变量 `y` 后使 `x` 无效。换句话说,这里没有深浅拷贝的区别,所以这里调用 `clone` 并不会与通常的浅拷贝有什么不同,我们可以不用管它。 原因是像整型这样的在编译时已知大小的类型被整个存储在栈上,所以拷贝其实际的值是快速的。这意味着没有理由在创建变量 `y` 后使 `x` 无效。换句话说,这里没有深浅拷贝的区别,所以这里调用 `clone` 并不会与通常的浅拷贝有什么不同,我们可以不用管它。
Rust 有一个叫做 `Copy` trait 的特殊注解,可以用在类似整型这样的存储在栈上的类型上(第十章详细讲解 trait。如果一个类型实现了 `Copy` trait那么一个旧的变量在将其赋值给其他变量后仍然可用。Rust 不允许自身或其任何部分实现了 `Drop` trait 的类型使用 `Copy` trait。如果我们对其值离开作用域时需要特殊处理的类型使用 `Copy` 注解,将会出现一个编译时错误。要学习如何为你的类型添加 `Copy` 注解以实现该 trait请阅读附录 C 中的 [“可派生的 trait”][derivable-traits]。 Rust 有一个叫做 `Copy` trait 的特殊注解,可以用在类似整型这样的存储在栈上的类型上([第十章][ch10]将会详细讲解 trait。如果一个类型实现了 `Copy` trait那么一个旧的变量在将其赋值给其他变量后仍然可用。
Rust 不允许自身或其任何部分实现了 `Drop` trait 的类型使用 `Copy` trait。如果我们对其值离开作用域时需要特殊处理的类型使用 `Copy` 注解,将会出现一个编译时错误。要学习如何为你的类型添加 `Copy` 注解以实现该 trait请阅读附录 C 中的 [“可派生的 trait”][derivable-traits]。
那么哪些类型实现了 `Copy` trait 呢?你可以查看给定类型的文档来确认,不过作为一个通用的规则,任何一组简单标量值的组合都可以实现 `Copy`,任何不需要分配内存或某种形式资源的类型都可以实现 `Copy` 。如下是一些 `Copy` 的类型: 那么哪些类型实现了 `Copy` trait 呢?你可以查看给定类型的文档来确认,不过作为一个通用的规则,任何一组简单标量值的组合都可以实现 `Copy`,任何不需要分配内存或某种形式资源的类型都可以实现 `Copy` 。如下是一些 `Copy` 的类型:
@ -209,7 +211,7 @@ Rust 有一个叫做 `Copy` trait 的特殊注解,可以用在类似整型这
### 所有权与函数 ### 所有权与函数
将值传递给函数在语义上与给变量赋值相似。向函数传递值可能会移动或者复制,就像赋值语句一样。示例 4-3 使用注释展示变量何时进入和离开作用域: 将值传递给函数与给变量赋值的原理相似。向函数传递值可能会移动或者复制,就像赋值语句一样。示例 4-3 使用注释展示变量何时进入和离开作用域:
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
@ -251,6 +253,7 @@ Rust 有一个叫做 `Copy` trait 的特殊注解,可以用在类似整型这
[data-types]: ch03-02-data-types.html#数据类型 [data-types]: ch03-02-data-types.html#数据类型
[ch8]: ch08-02-strings.html [ch8]: ch08-02-strings.html
[ch10]: ch10-00-generics.md
[derivable-traits]: appendix-03-derivable-traits.html [derivable-traits]: appendix-03-derivable-traits.html
[method-syntax]: ch05-03-method-syntax.html#方法语法 [method-syntax]: ch05-03-method-syntax.html#方法语法
[paths-module-tree]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html [paths-module-tree]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html

@ -2,9 +2,12 @@
> [ch04-02-references-and-borrowing.md](https://github.com/rust-lang/book/blob/main/src/ch04-02-references-and-borrowing.md) > [ch04-02-references-and-borrowing.md](https://github.com/rust-lang/book/blob/main/src/ch04-02-references-and-borrowing.md)
> <br> > <br>
> commit 8cf0496bb8e56b683ea3f015871c8631684decf4 > commit d82136cbdc91c598ef9997493aa577b1a349565e
示例 4-5 中的元组代码有这样一个问题:我们必须将 `String` 返回给调用函数,以便在调用 `calculate_length` 后仍能使用 `String`,因为 `String` 被移动到了 `calculate_length` 内。相反我们可以提供一个 `String` 值的引用reference。**引用***reference*)像一个指针,因为它是一个地址,我们可以由此访问储存于该地址的属于其他变量的数据。与指针不同,引用确保指向某个特定类型的有效值。下面是如何定义并使用一个(新的)`calculate_length` 函数,它以一个对象的引用作为参数而不是获取值的所有权: 示例 4-5 中的元组代码有这样一个问题:我们必须将 `String` 返回给调用函数,以便在调用 `calculate_length` 后仍能使用 `String`,因为 `String` 被移动到了 `calculate_length` 内。相反我们可以提供一个 `String` 值的引用reference。**引用***reference*)像一个指针,因为它是一个地址,我们可以由此访问储存于该地址的属于其他变量的数据。
与指针不同,引用确保指向某个特定类型的有效值。
下面是如何定义并使用一个(新的)`calculate_length` 函数,它以一个对象的引用作为参数而不是获取值的所有权:
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
@ -14,7 +17,7 @@
首先,注意变量声明和函数返回值中的所有元组代码都消失了。其次,注意我们传递 `&s1``calculate_length`,同时在函数定义中,我们获取 `&String` 而不是 `String`。这些 & 符号就是 **引用**,它们允许你使用值但不获取其所有权。图 4-5 展示了一张示意图。 首先,注意变量声明和函数返回值中的所有元组代码都消失了。其次,注意我们传递 `&s1``calculate_length`,同时在函数定义中,我们获取 `&String` 而不是 `String`。这些 & 符号就是 **引用**,它们允许你使用值但不获取其所有权。图 4-5 展示了一张示意图。
<img alt="&String s pointing at String s1" src="img/trpl04-05.svg" class="center" /> <img alt="&amp;String s pointing at String s1" src="img/trpl04-05.svg" class="center" />
<span class="caption">图 4-5`&String s` 指向 `String s1` 示意图</span> <span class="caption">图 4-5`&String s` 指向 `String s1` 示意图</span>
@ -68,7 +71,7 @@
首先,我们必须将 `s` 改为 `mut`。然后在调用 `change` 函数的地方创建一个可变引用 `&mut s`,并更新函数签名以接受一个可变引用 `some_string: &mut String`。这就非常清楚地表明,`change` 函数将改变它所借用的值。 首先,我们必须将 `s` 改为 `mut`。然后在调用 `change` 函数的地方创建一个可变引用 `&mut s`,并更新函数签名以接受一个可变引用 `some_string: &mut String`。这就非常清楚地表明,`change` 函数将改变它所借用的值。
可变引用有一个很大的限制:在同一时间只能有一个对某一特定数据的可变引用。这些尝试创建两个 `s` 的可变引用的代码会失败: 可变引用有一个很大的限制:如果你有一个对该变量的可变引用,你就不能再创建对该变量的引用。这些尝试创建两个 `s` 的可变引用的代码会失败:
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
@ -110,7 +113,9 @@ Rust 在同时使用可变与不可变引用时也采用的类似的规则。这
{{#include ../listings/ch04-understanding-ownership/no-listing-12-immutable-and-mutable-not-allowed/output.txt}} {{#include ../listings/ch04-understanding-ownership/no-listing-12-immutable-and-mutable-not-allowed/output.txt}}
``` ```
哇哦!我们 **也** 不能在拥有不可变引用的同时拥有可变引用。不可变引用的用户可不希望在他们的眼皮底下值就被意外的改变了!然而,多个不可变引用是可以的,因为没有哪个只能读取数据的人有能力影响其他人读取到的数据。 哇哦!我们 **也** 不能在拥有不可变引用的同时拥有可变引用。
不可变引用的用户可不希望在他们的眼皮底下值就被意外的改变了!然而,多个不可变引用是可以的,因为没有哪个只能读取数据的人有能力影响其他人读取到的数据。
注意一个引用的作用域从声明的地方开始一直持续到最后一次使用为止。例如,因为最后一次使用不可变引用(`println!`),发生在声明可变引用之前,所以如下代码是可以编译的: 注意一个引用的作用域从声明的地方开始一直持续到最后一次使用为止。例如,因为最后一次使用不可变引用(`println!`),发生在声明可变引用之前,所以如下代码是可以编译的:

@ -6,7 +6,7 @@
*slice* 允许你引用集合中一段连续的元素序列而不用引用整个集合。slice 是一类引用,所以它没有所有权。 *slice* 允许你引用集合中一段连续的元素序列而不用引用整个集合。slice 是一类引用,所以它没有所有权。
这里有一个编程小习题:编写一个函数,该函数接收一个字符串,并返回在该字符串中找到的第一个单词。如果函数在该字符串中并未找到空格,则整个字符串就是一个单词,所以应该返回整个字符串。 这里有一个编程小习题:编写一个函数,该函数接收一个用空格分隔单词的字符串,并返回在该字符串中找到的第一个单词。如果函数在该字符串中并未找到空格,则整个字符串就是一个单词,所以应该返回整个字符串。
让我们推敲下如何不用 slice 编写这个函数的签名,来理解 slice 能解决的问题: 让我们推敲下如何不用 slice 编写这个函数的签名,来理解 slice 能解决的问题:

Loading…
Cancel
Save