mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-13 03:21:20 +08:00
check to ch05-00
This commit is contained in:
parent
f8fe51658a
commit
257515819d
@ -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>
|
||||
> commit f949ff883628db8ed2f2f5f19e146ebf19ed6a6f
|
||||
> commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37
|
||||
|
||||
Rust 的核心功能(之一)是 **所有权**(*ownership*)。虽然该功能很容易解释,但它对语言的其他部分有着深刻的影响。
|
||||
|
||||
@ -165,11 +165,11 @@ let s2 = s1;
|
||||
|
||||
<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 s2 = s1;
|
||||
|
||||
@ -254,7 +254,7 @@ Rust 有一个叫做 `Copy` trait 的特殊注解,可以用在类似整型这
|
||||
fn main() {
|
||||
let s = String::from("hello"); // s 进入作用域
|
||||
|
||||
takes_ownership(s); // s 的值移动到函数里...
|
||||
takes_ownership(s); // s 的值移动到函数里 ...
|
||||
// ... 所以到这里不再有效
|
||||
|
||||
let x = 5; // x 进入作用域
|
||||
|
@ -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>
|
||||
> commit f949ff883628db8ed2f2f5f19e146ebf19ed6a6f
|
||||
> commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37
|
||||
|
||||
示例 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>
|
||||
|
||||
```rust,ignore
|
||||
```rust,ignore,does_not_compile
|
||||
fn main() {
|
||||
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 r1 = &mut s;
|
||||
let r2 = &mut s;
|
||||
|
||||
println!("{}, {}", r1, r2);
|
||||
```
|
||||
|
||||
|
||||
错误如下:
|
||||
|
||||
```text
|
||||
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;
|
||||
| - first mutable borrow occurs here
|
||||
5 | let r2 = &mut s;
|
||||
| ^ second mutable borrow occurs here
|
||||
6 | }
|
||||
| - first borrow ends here
|
||||
4 | let r1 = &mut s;
|
||||
| ------ first mutable borrow occurs here
|
||||
5 | let r2 = &mut s;
|
||||
| ^^^^^^ second mutable borrow occurs here
|
||||
6 | println!("{}, {}", r1, r2);
|
||||
| -- borrow later used here
|
||||
```
|
||||
|
||||
这个限制允许可变性,不过是以一种受限制的方式允许。新 Rustacean 们经常与此作斗争,因为大部分语言中变量任何时候都是可变的。
|
||||
@ -160,28 +165,30 @@ let r2 = &mut s;
|
||||
|
||||
类似的规则也存在于同时使用可变与不可变引用中。这些代码会导致一个错误:
|
||||
|
||||
```rust,ignore
|
||||
```rust,ignore,does_not_compile
|
||||
let mut s = String::from("hello");
|
||||
|
||||
let r1 = &s; // no problem
|
||||
let r2 = &s; // no problem
|
||||
let r3 = &mut s; // BIG PROBLEM
|
||||
|
||||
println!("{}, {}, and {}", r1, r2, r3);
|
||||
```
|
||||
|
||||
错误如下:
|
||||
|
||||
```text
|
||||
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as
|
||||
immutable
|
||||
--> borrow_thrice.rs:6:19
|
||||
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
|
||||
--> src/main.rs:6:10
|
||||
|
|
||||
4 | let r1 = &s; // no problem
|
||||
| - immutable borrow occurs here
|
||||
5 | let r2 = &s; // no problem
|
||||
6 | let r3 = &mut s; // BIG PROBLEM
|
||||
| ^ mutable borrow occurs here
|
||||
7 | }
|
||||
| - immutable borrow ends here
|
||||
4 | let r1 = &s; // no problem
|
||||
| -- immutable borrow occurs here
|
||||
5 | let r2 = &s; // no problem
|
||||
6 | let r3 = &mut s; // BIG PROBLEM
|
||||
| ^^^^^^ mutable borrow occurs here
|
||||
7 |
|
||||
8 | println!("{}, {}, and {}", r1, r2, r3);
|
||||
| -- borrow later used here
|
||||
```
|
||||
|
||||
哇哦!我们 **也** 不能在拥有不可变引用的同时拥有可变引用。不可变引用的用户可不希望在他们的眼皮底下值就被意外的改变了!然而,多个不可变引用是可以的,因为没有哪个只能读取数据的人有能力影响其他人读取到的数据。
|
||||
@ -196,7 +203,7 @@ immutable
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
```rust,ignore,does_not_compile
|
||||
fn main() {
|
||||
let reference_to_nothing = dangle();
|
||||
}
|
||||
@ -231,15 +238,13 @@ for it to be borrowed from.
|
||||
|
||||
让我们仔细看看我们的 `dangle` 代码的每一步到底发生了什么:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
```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 离开作用域并被丢弃。其内存被释放。
|
||||
// 危险!
|
||||
```
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
## 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>
|
||||
> commit 729f1118332ddcf01138d284eac5db0e85f8c8ed
|
||||
> commit a86c1d315789b3ca13b20d50ad5005c62bdd9e37
|
||||
|
||||
另一个没有所有权的数据类型是 *slice*。slice 允许你引用集合中一段连续的元素序列,而不用引用整个集合。
|
||||
|
||||
@ -115,9 +115,18 @@ let hello = &s[0..5];
|
||||
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 展示了一个图例。
|
||||
|
||||
@ -190,13 +199,15 @@ fn second_word(s: &String) -> &str {
|
||||
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust,ignore
|
||||
```rust,ignore,does_not_compile
|
||||
fn main() {
|
||||
let mut s = String::from("hello world");
|
||||
|
||||
let word = first_word(&s);
|
||||
|
||||
s.clear(); // error!
|
||||
|
||||
println!("the first word is: {}", word);
|
||||
}
|
||||
```
|
||||
|
||||
@ -204,15 +215,16 @@ fn main() {
|
||||
|
||||
```text
|
||||
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
|
||||
--> src/main.rs:6:5
|
||||
|
|
||||
4 | let word = first_word(&s);
|
||||
| - immutable borrow occurs here
|
||||
5 |
|
||||
6 | s.clear(); // error!
|
||||
| ^ mutable borrow occurs here
|
||||
7 | }
|
||||
| - immutable borrow ends here
|
||||
--> src/main.rs:10:5
|
||||
|
|
||||
8 | let word = first_word(&s);
|
||||
| -- immutable borrow occurs here
|
||||
9 |
|
||||
10 | s.clear(); // error!
|
||||
| ^^^^^^^^^ mutable borrow occurs here
|
||||
11 |
|
||||
12 | println!("the first word is: {}", word);
|
||||
| ---- borrow later used here
|
||||
```
|
||||
|
||||
回忆一下借用规则,当拥有某值的不可变引用时,就不能再获取一个可变引用。因为 `clear` 需要清空 `String`,它尝试获取一个可变引用,它失败了。Rust 不仅使得我们的 API 简单易用,也在编译时就消除了一整类的错误!
|
||||
@ -245,7 +257,7 @@ fn first_word(s: &str) -> &str {
|
||||
|
||||
如果有一个字符串 slice,可以直接传递它。如果有一个 `String`,则可以传递整个 `String` 的 slice。定义一个获取字符串 slice 而不是 `String` 引用的函数使得我们的 API 更加通用并且不会丢失任何功能:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
<span class="filename">文件名: src/main.rs</span>
|
||||
|
||||
```rust
|
||||
# fn first_word(s: &str) -> &str {
|
||||
|
@ -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>
|
||||
> commit 5e2368866cb445d912f5d2a2c92f4b42bcb542bb
|
||||
> commit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f
|
||||
|
||||
*struct*,或者 *structure*,是一个自定义数据类型,允许你命名和包装多个相关的值,从而形成一个有意义的组合。如果你熟悉一门面向对象语言,*struct* 就像对象中的数据属性。在本章中,我们会对比元组与结构体的异同,演示结构体的用法,并讨论如何在结构体上定义方法和关联函数来指定与结构体数据相关的行为。你在程序中基于结构体和枚举(*enum*)(在第六章介绍)创建新类型,以充分利用 Rust 的编译时类型检查。
|
||||
|
Loading…
Reference in New Issue
Block a user