check to ch05-00

This commit is contained in:
KaiserY 2018-12-02 21:03:07 +08:00
parent f8fe51658a
commit 257515819d
4 changed files with 69 additions and 52 deletions

View File

@ -1,8 +1,8 @@
## 什么是所有权 ## 什么是所有权
> [ch04-01-what-is-ownership.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch04-01-what-is-ownership.md) > [ch04-01-what-is-ownership.md](https://github.com/rust-lang/book/blob/master/src/ch04-01-what-is-ownership.md)
> <br> > <br>
> commit f949ff883628db8ed2f2f5f19e146ebf19ed6a6f > commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37
Rust 的核心功能(之一)是 **所有权***ownership*)。虽然该功能很容易解释,但它对语言的其他部分有着深刻的影响。 Rust 的核心功能(之一)是 **所有权***ownership*)。虽然该功能很容易解释,但它对语言的其他部分有着深刻的影响。
@ -165,11 +165,11 @@ let s2 = s1;
<span class="caption">图 4-3另一个 `s2 = s1` 时可能的内存表现,如果 Rust 同时也拷贝了堆上的数据的话</span> <span class="caption">图 4-3另一个 `s2 = s1` 时可能的内存表现,如果 Rust 同时也拷贝了堆上的数据的话</span>
之前我们提到过当变量离开作用域后Rust 自动调用 `drop` 函数并清理变量的堆内存。不过图 4-2 展示了两个数据指针指向了同一位置。这就有了一个问题:当 `s2``s1` 离开作用域,他们都会尝试释放相同的内存。这是一个叫做 **二次释放***double free*)的错误,也是之前提到过的内存安全性 bug 之一。两次释放(相同)内存会导致内存污染,它可能会导致潜在的安全漏洞。 之前我们提到过当变量离开作用域后Rust 自动调用 `drop` 函数并清理变量的堆内存。不过图 4-2 展示了两个数据指针指向了同一位置。这就有了一个问题:当 `s2``s1` 离开作用域,他们都会尝试释放相同的内存。这是一个叫做 **二次释放***double free*)的错误,也是之前提到过的内存安全性 bug 之一。两次释放(相同)内存会导致内存污染,它可能会导致潜在的安全漏洞。
为了确保内存安全,这种场景下 Rust 的处理有另一个细节值得注意。与其尝试拷贝被分配的内存Rust 则认为 `s1` 不再有效,因此 Rust 不需要在 `s1` 离开作用域后清理任何东西。看看在 `s2` 被创建之后尝试使用 `s1` 会发生什么;它不会工作 为了确保内存安全,这种场景下 Rust 的处理有另一个细节值得注意。与其尝试拷贝被分配的内存Rust 则认为 `s1` 不再有效,因此 Rust 不需要在 `s1` 离开作用域后清理任何东西。看看在 `s2` 被创建之后尝试使用 `s1` 会发生什么;这段代码不能运行
```rust,ignore ```rust,ignore,does_not_compile
let s1 = String::from("hello"); let s1 = String::from("hello");
let s2 = s1; let s2 = s1;
@ -254,7 +254,7 @@ Rust 有一个叫做 `Copy` trait 的特殊注解,可以用在类似整型这
fn main() { fn main() {
let s = String::from("hello"); // s 进入作用域 let s = String::from("hello"); // s 进入作用域
takes_ownership(s); // s 的值移动到函数里... takes_ownership(s); // s 的值移动到函数里 ...
// ... 所以到这里不再有效 // ... 所以到这里不再有效
let x = 5; // x 进入作用域 let x = 5; // x 进入作用域

View File

@ -1,8 +1,8 @@
## 引用与借用 ## 引用与借用
> [ch04-02-references-and-borrowing.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch04-02-references-and-borrowing.md) > [ch04-02-references-and-borrowing.md](https://github.com/rust-lang/book/blob/master/src/ch04-02-references-and-borrowing.md)
> <br> > <br>
> commit f949ff883628db8ed2f2f5f19e146ebf19ed6a6f > commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37
示例 4-5 中的元组代码有这样一个问题:我们必须将 `String` 返回给调用函数,以便在调用 `calculate_length` 后仍能使用 `String`,因为 `String` 被移动到了 `calculate_length` 内。 示例 4-5 中的元组代码有这样一个问题:我们必须将 `String` 返回给调用函数,以便在调用 `calculate_length` 后仍能使用 `String`,因为 `String` 被移动到了 `calculate_length` 内。
@ -64,7 +64,7 @@ fn calculate_length(s: &String) -> usize { // s 是对 String 的引用
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
```rust,ignore ```rust,ignore,does_not_compile
fn main() { fn main() {
let s = String::from("hello"); let s = String::from("hello");
@ -114,25 +114,30 @@ fn change(some_string: &mut String) {
不过可变引用有一个很大的限制:在特定作用域中的特定数据有且只有一个可变引用。这些代码会失败: 不过可变引用有一个很大的限制:在特定作用域中的特定数据有且只有一个可变引用。这些代码会失败:
```rust,ignore <span class="filename">文件名: src/main.rs</span>
```rust,ignore,does_not_compile
let mut s = String::from("hello"); let mut s = String::from("hello");
let r1 = &mut s; let r1 = &mut s;
let r2 = &mut s; let r2 = &mut s;
println!("{}, {}", r1, r2);
``` ```
错误如下: 错误如下:
```text ```text
error[E0499]: cannot borrow `s` as mutable more than once at a time error[E0499]: cannot borrow `s` as mutable more than once at a time
--> borrow_twice.rs:5:19 --> src/main.rs:5:10
| |
4 | let r1 = &mut s; 4 | let r1 = &mut s;
| - first mutable borrow occurs here | ------ first mutable borrow occurs here
5 | let r2 = &mut s; 5 | let r2 = &mut s;
| ^ second mutable borrow occurs here | ^^^^^^ second mutable borrow occurs here
6 | } 6 | println!("{}, {}", r1, r2);
| - first borrow ends here | -- borrow later used here
``` ```
这个限制允许可变性,不过是以一种受限制的方式允许。新 Rustacean 们经常与此作斗争,因为大部分语言中变量任何时候都是可变的。 这个限制允许可变性,不过是以一种受限制的方式允许。新 Rustacean 们经常与此作斗争,因为大部分语言中变量任何时候都是可变的。
@ -160,28 +165,30 @@ let r2 = &mut s;
类似的规则也存在于同时使用可变与不可变引用中。这些代码会导致一个错误: 类似的规则也存在于同时使用可变与不可变引用中。这些代码会导致一个错误:
```rust,ignore ```rust,ignore,does_not_compile
let mut s = String::from("hello"); let mut s = String::from("hello");
let r1 = &s; // no problem let r1 = &s; // no problem
let r2 = &s; // no problem let r2 = &s; // no problem
let r3 = &mut s; // BIG PROBLEM let r3 = &mut s; // BIG PROBLEM
println!("{}, {}, and {}", r1, r2, r3);
``` ```
错误如下: 错误如下:
```text ```text
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
immutable --> src/main.rs:6:10
--> borrow_thrice.rs:6:19
| |
4 | let r1 = &s; // no problem 4 | let r1 = &s; // no problem
| - immutable borrow occurs here | -- immutable borrow occurs here
5 | let r2 = &s; // no problem 5 | let r2 = &s; // no problem
6 | let r3 = &mut s; // BIG PROBLEM 6 | let r3 = &mut s; // BIG PROBLEM
| ^ mutable borrow occurs here | ^^^^^^ mutable borrow occurs here
7 | } 7 |
| - immutable borrow ends here 8 | println!("{}, {}, and {}", r1, r2, r3);
| -- borrow later used here
``` ```
哇哦!我们 **也** 不能在拥有不可变引用的同时拥有可变引用。不可变引用的用户可不希望在他们的眼皮底下值就被意外的改变了!然而,多个不可变引用是可以的,因为没有哪个只能读取数据的人有能力影响其他人读取到的数据。 哇哦!我们 **也** 不能在拥有不可变引用的同时拥有可变引用。不可变引用的用户可不希望在他们的眼皮底下值就被意外的改变了!然而,多个不可变引用是可以的,因为没有哪个只能读取数据的人有能力影响其他人读取到的数据。
@ -196,7 +203,7 @@ immutable
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
```rust,ignore ```rust,ignore,does_not_compile
fn main() { fn main() {
let reference_to_nothing = dangle(); let reference_to_nothing = dangle();
} }
@ -231,15 +238,13 @@ for it to be borrowed from.
让我们仔细看看我们的 `dangle` 代码的每一步到底发生了什么: 让我们仔细看看我们的 `dangle` 代码的每一步到底发生了什么:
<span class="filename">Filename: src/main.rs</span>
```rust,ignore ```rust,ignore
fn dangle() -> &String { // dangle 返回一个 String 引用 fn dangle() -> &String { // dangle 返回一个字符串的引用
let s = String::from("hello"); // s 是新创建的 String let s = String::from("hello"); // s 是一个新字符串
&s // 我们返回 String 的引用s &s // 返回字符串 s 的引用
} // 这里s 移出了作用域,并被丢弃。它的内存被释放 } // 这里 s 离开作用域并被丢弃。其内存被释放。
// 危险! // 危险!
``` ```

View File

@ -1,8 +1,8 @@
## Slice 类型 ## Slice 类型
> [ch04-03-slices.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch04-03-slices.md) > [ch04-03-slices.md](https://github.com/rust-lang/book/blob/master/src/ch04-03-slices.md)
> <br> > <br>
> commit 729f1118332ddcf01138d284eac5db0e85f8c8ed > commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37
另一个没有所有权的数据类型是 *slice*。slice 允许你引用集合中一段连续的元素序列,而不用引用整个集合。 另一个没有所有权的数据类型是 *slice*。slice 允许你引用集合中一段连续的元素序列,而不用引用整个集合。
@ -115,9 +115,18 @@ let hello = &s[0..5];
let world = &s[6..11]; let world = &s[6..11];
``` ```
这类似于引用整个 `String` 不过带有额外的 `[0..5]` 部分。不是对整个 `String` 的引用,而是对部分 `String` 的引用。`start..end` 语法代表一个以 `start` 开头并一直持续到但不包含 `end` 的 range。 这类似于引用整个 `String` 不过带有额外的 `[0..5]` 部分。不是对整个 `String` 的引用,而是对部分 `String` 的引用。`start..end` 语法代表一个以 `start` 开头并一直持续到但不包含 `end` 的 range。如果需要包含 `end`,可以使用 `..=` 而不是 `..`
使用一个由中括号中的 `[starting_index..ending_index]` 指定的 range 创建一个 slice其中 `starting_index` 是 slice 的第一个位置,`ending_index` 则是 slice 最后一个位置的后一个值。在其内部slice 的数据结构存储了 slice 的开始位置和长度,长度对应于 `ending_index` 减去 `starting_index` 的值。所以对于 `let world = &s[6..11];` 的情况,`world` 将是一个包含指向 `s` 第 7 个字节的指针和长度值 5 的 slice。 ```rust
let s = String::from("hello world");
let hello = &s[0..=4];
let world = &s[6..=10];
```
`=` 意味着包含最后的数字,这有助于你记住 `..``..=` 的区别
可以使用一个由中括号中的 `[starting_index..ending_index]` 指定的 range 创建一个 slice其中 `starting_index` 是 slice 的第一个位置,`ending_index` 则是 slice 最后一个位置的后一个值。在其内部slice 的数据结构存储了 slice 的开始位置和长度,长度对应于 `ending_index` 减去 `starting_index` 的值。所以对于 `let world = &s[6..11];` 的情况,`world` 将是一个包含指向 `s` 第 7 个字节的指针和长度值 5 的 slice。
图 4-6 展示了一个图例。 图 4-6 展示了一个图例。
@ -190,13 +199,15 @@ fn second_word(s: &String) -> &str {
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
```rust,ignore ```rust,ignore,does_not_compile
fn main() { fn main() {
let mut s = String::from("hello world"); let mut s = String::from("hello world");
let word = first_word(&s); let word = first_word(&s);
s.clear(); // error! s.clear(); // error!
println!("the first word is: {}", word);
} }
``` ```
@ -204,15 +215,16 @@ fn main() {
```text ```text
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
--> src/main.rs:6:5 --> src/main.rs:10:5
| |
4 | let word = first_word(&s); 8 | let word = first_word(&s);
| - immutable borrow occurs here | -- immutable borrow occurs here
5 | 9 |
6 | s.clear(); // error! 10 | s.clear(); // error!
| ^ mutable borrow occurs here | ^^^^^^^^^ mutable borrow occurs here
7 | } 11 |
| - immutable borrow ends here 12 | println!("the first word is: {}", word);
| ---- borrow later used here
``` ```
回忆一下借用规则,当拥有某值的不可变引用时,就不能再获取一个可变引用。因为 `clear` 需要清空 `String`它尝试获取一个可变引用它失败了。Rust 不仅使得我们的 API 简单易用,也在编译时就消除了一整类的错误! 回忆一下借用规则,当拥有某值的不可变引用时,就不能再获取一个可变引用。因为 `clear` 需要清空 `String`它尝试获取一个可变引用它失败了。Rust 不仅使得我们的 API 简单易用,也在编译时就消除了一整类的错误!
@ -245,7 +257,7 @@ fn first_word(s: &str) -> &str {
如果有一个字符串 slice可以直接传递它。如果有一个 `String`,则可以传递整个 `String` 的 slice。定义一个获取字符串 slice 而不是 `String` 引用的函数使得我们的 API 更加通用并且不会丢失任何功能: 如果有一个字符串 slice可以直接传递它。如果有一个 `String`,则可以传递整个 `String` 的 slice。定义一个获取字符串 slice 而不是 `String` 引用的函数使得我们的 API 更加通用并且不会丢失任何功能:
<span class="filename">Filename: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>
```rust ```rust
# fn first_word(s: &str) -> &str { # fn first_word(s: &str) -> &str {

View File

@ -1,7 +1,7 @@
# 使用结构体组织相关联的数据 # 使用结构体组织相关联的数据
> [ch05-00-structs.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch05-00-structs.md) > [ch05-00-structs.md](https://github.com/rust-lang/book/blob/master/src/ch05-00-structs.md)
> <br> > <br>
> commit 5e2368866cb445d912f5d2a2c92f4b42bcb542bb > commit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f
*struct*,或者 *structure*,是一个自定义数据类型,允许你命名和包装多个相关的值,从而形成一个有意义的组合。如果你熟悉一门面向对象语言,*struct* 就像对象中的数据属性。在本章中,我们会对比元组与结构体的异同,演示结构体的用法,并讨论如何在结构体上定义方法和关联函数来指定与结构体数据相关的行为。你在程序中基于结构体和枚举(*enum*)(在第六章介绍)创建新类型,以充分利用 Rust 的编译时类型检查。 *struct*,或者 *structure*,是一个自定义数据类型,允许你命名和包装多个相关的值,从而形成一个有意义的组合。如果你熟悉一门面向对象语言,*struct* 就像对象中的数据属性。在本章中,我们会对比元组与结构体的异同,演示结构体的用法,并讨论如何在结构体上定义方法和关联函数来指定与结构体数据相关的行为。你在程序中基于结构体和枚举(*enum*)(在第六章介绍)创建新类型,以充分利用 Rust 的编译时类型检查。