wip: 2024 edition

pull/859/head
kazeno 6 days ago
parent 51c0c6f419
commit 9a7f193bde

@ -14,34 +14,13 @@
- 英文原文:
```
Notice that we dont include the `unsafe` keyword in this code. We can create
raw pointers in safe code; we just cant dereference raw pointers outside an
unsafe block, as youll see in a bit.
Weve created raw pointers by using the raw borrow operators: `&raw const num`
creates a `*const i32` immutable raw pointer, and `&raw mut num` creates a `*mut
i32` mutable raw pointer. Because we created them directly from a local
variable, we know these particular raw pointers are valid, but we cant make
that assumption about just any raw pointer.
To demonstrate this, next well create a raw pointer whose validity we cant be
so certain of, using `as` to cast a value instead of using the raw borrow
operators. Listing 20-2 shows how to create a raw pointer to an arbitrary
location in memory. Trying to use arbitrary memory is undefined: there might be
data at that address or there might not, the compiler might optimize the code so
there is no memory access, or the program might terminate with a segmentation
fault. Usually, there is no good reason to write code like this, especially in
cases where you can use a raw borrow operator instead, but it is possible.
```
- 中文翻译:
```
注意这里没有引入 `unsafe` 关键字。可以在安全代码中 **创建** 裸指针,只是不能在不安全块之外 **解引用** 裸指针,稍后便会看到。
这里使用 `as` 将不可变和可变引用强转为对应的裸指针类型。因为直接从保证安全的引用来创建它们,可以知道这些特定的裸指针是有效,但是不能对任何裸指针做出如此假设。
作为展示接下来会创建一个不能确定其有效性的裸指针,示例 19-2 展示了如何创建一个指向任意内存地址的裸指针。尝试使用任意内存是未定义行为此地址可能有数据也可能没有编译器可能会优化掉这个内存访问或者程序可能会出现段错误segmentation fault。通常没有好的理由编写这样的代码不过却是可行的
```
**输出:**

@ -91,4 +91,4 @@
[creating-instances-from-other-instances-with-struct-update-syntax]: ch05-01-defining-structs.html#使用结构体更新语法从其他实例创建实例
[stack-only-data-copy]: ch04-01-what-is-ownership.html#只在栈上的数据拷贝
[ways-variables-and-data-interact-clone]: ch04-01-what-is-ownership.html#变量与数据交互的方式二克隆
[macros]: ch20-06-macros.html#宏
[macros]: ch20-05-macros.html#宏

@ -128,6 +128,6 @@
[performance-of-code-using-generics]:
ch10-01-syntax.html#泛型代码的性能
[dynamically-sized]: ch20-04-advanced-types.html#动态大小类型和-sized-trait
[dynamically-sized]: ch20-03-advanced-types.html#动态大小类型和-sized-trait
[Rust RFC 255 ref]: https://github.com/rust-lang/rfcs/blob/master/text/0255-object-safety.md
[Rust Reference ref]: https://doc.rust-lang.org/reference/items/traits.html#object-safety

@ -248,4 +248,4 @@
接下来,让我们看看另一个提供了多样灵活性的 Rust 功能:模式。贯穿全书的模式,我们已经和它们打过照面了,但并没有见识过它们的全部本领。让我们开始探索吧!
[more-info-than-rustc]: ch09-03-to-panic-or-not-to-panic.html#当我们比编译器知道更多的情况
[macros]: ch20-06-macros.html#宏
[macros]: ch20-05-macros.html#宏

@ -63,7 +63,7 @@
<span class="caption">示例 20-2: 创建指向任意内存地址的裸指针</span>
记得我们说过可以在安全代码中创建裸指针,不过不能 **解引用** 裸指针和读取其指向的数据。现在我们要做的就是对裸指针使用解引用运算符 `*`,这需要一个 `unsafe` 块,如示例 20-3 所示
记得我们说过可以在安全代码中创建裸指针,但不能 **解引用** 裸指针和读取其指向的数据。示例 20-3 中,我们在裸指针上使用了解引用运算符 `*`,该操作需要一个 `unsafe`
```rust
{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-03/src/main.rs:here}}
@ -71,15 +71,15 @@
<span class="caption">示例 20-3: 在 `unsafe` 块中解引用裸指针</span>
创建一个指针不会造成任何危;只有当访问其指向的值时才有可能遇到无效的值。
创建一个指针不会造成任何危;只有当访问其指向的值时才有可能遇到无效的值。
还需注意示例 20-1 和 20-3 中创建了同时指向相同内存位置 `num` 的裸指针 `*const i32``*mut i32`。相反如果尝试同时创建 `num` 的不可变和可变引用,将无法通过编译,因为 Rust 的所有权规则不允许在拥有任何不可变引用的同时再创建一个可变引用。通过裸指针,就能够同时创建同一地址的可变指针和不可变指针,若通过可变指针修改数据,则可能潜在造成数据竞争。请多加小心!
还需注意示例 20-1 和 20-3 中创建了同时指向相同内存位置 `num` 的裸指针 `*const i32``*mut i32`。相反如果尝试同时创建 `num` 的不可变和可变引用,代码将无法通过编译,因为 Rust 的所有权规则不允许在拥有任何不可变引用的同时再创建可变引用。通过裸指针,就能够同时创建同一地址的可变指针和不可变指针,若通过可变指针修改数据,则可能潜在造成数据竞争。请多加小心!
既然存在这么多的危险,为何还要使用裸指针呢?一个主要的应用场景便是调用 C 代码接口,这在下一部分 [“调用不安全函数或方法”](#调用不安全函数或方法) 中会讲到。另一个场景是构建借用检查器无法理解的安全抽象。让我们先介绍不安全函数,接着看一看使用不安全代码的安全抽象的例
既然存在这么多的危险,为何还要使用裸指针呢?一个主要的应用场景便是调用 C 代码接口,这在下一部分 [“调用不安全函数或方法”](#调用不安全函数或方法) 中会讲到。另一个场景是构建借用检查器无法理解的安全抽象。让我们先介绍不安全函数,接着看一看使用不安全代码的安全抽象的例。
### 调用不安全函数或方法
第二类可以在不安全块中进行的操作是调用不安全函数。不安全函数和方法与常规函数方法十分类似,除了其开头有一个额外的 `unsafe`。在此上下文中,关键字`unsafe`表示该函数具有调用时需要满足的要求,而 Rust 不会保证满足这些要求。通过在 `unsafe` 块中调用不安全函数,表明我们已经阅读过此函数的文档并对其是否满足函数自身的契约负责。
第二类可以在不安全块中进行的操作是调用不安全函数。不安全函数和方法与常规函数方法十分类似,除了其开头有一个额外的 `unsafe`。在此上下文中,关键字 `unsafe` 表示该函数具有调用时需要满足的要求,而 Rust 不会保证满足这些要求。通过在 `unsafe` 块中调用不安全函数,表明我们已经阅读过此函数的文档并对其是否满足函数自身的契约负责。
如下是一个没有做任何操作的不安全函数 `dangerous` 的例子:
@ -93,13 +93,15 @@
{{#include ../listings/ch20-advanced-features/output-only-01-missing-unsafe/output.txt}}
```
通过 `unsafe` 块,我们向 Rust 保证了我们已经阅读过函数的文档,理解如何正确使用,并验证过其满足函数的契约。
通过 `unsafe` 块,我们向 Rust 断言我们已经阅读过函数的文档,理解如何正确使用它,并核实我们履行了该函数的契约。
在不安全函数的函数体内部执行不安全操作时,同样需要使用 `unsafe` 块,就像在普通函数中一样,如果忘记了编译器会发出警告。这有助于将 `unsafe` 块保持得尽可能小,因为 `unsafe` 操作并不一定需要覆盖整个函数体。
不安全函数体也是有效的 `unsafe` 块,所以在不安全函数中进行另一个不安全操作时无需新增额外的 `unsafe` 块。
#### 创建不安全代码的安全抽象
仅仅因为函数包含不安全代码并不意味着整个函数都需要标记为不安全的。事实上,将不安全代码封装进安全函数是一个常见的抽象。作为一个例子,了解一下标准库中的函数 `split_at_mut`,它需要一些不安全代码,让我们探索如何可以实现它。这个安全函数定义于可变 slice 之上:它获取一个 slice 并从给定的索引参数开始将其分为两个 slice。`split_at_mut` 的用法如示例 20-4 所示:
仅仅因为函数包含不安全代码并不意味着整个函数都需要标记为不安全的。事实上,将不安全代码封装进安全函数是一种常见的抽象方式。作为一个例子,了解一下标准库中的函数 `split_at_mut`,它需要一些不安全代码,让我们探索如何可以实现它。这个安全函数定义于可变 slice 之上:它获取一个 slice 并从给定的索引参数开始将其分割为两个 slice。示例 20-4 展示了如何使用 `split_at_mut`
```rust
{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-04/src/main.rs:here}}
@ -125,7 +127,7 @@
{{#include ../listings/ch20-advanced-features/listing-20-05/output.txt}}
```
Rust 的借用检查器不能理解我们要借用这个 slice 的两个不同部分:它只知道我们借用了同一个 slice 两次。本质上借用 slice 的不同部分是可以的,因为结果两个 slice 不会重叠,不过 Rust 还没有智能到能够理解这些。当我们知道某些事是可以的而 Rust 不知道的时候,就是触及不安全代码的时候了
Rust 的借用检查器无法理解我们要借用这个 slice 的两个不同部分:它只知道我们借用了同一个 slice 两次。本质上借用 slice 的不同部分是可以的,因为这两段 slice 不会重叠,不过 Rust 还没有智能到能够理解这些。当我们知道某些事是可以的而 Rust 不知道的时候,就是触及不安全代码的时候了
示例 20-6 展示了如何使用 `unsafe` 块,裸指针和一些不安全函数调用来实现 `split_at_mut`
@ -135,15 +137,15 @@ Rust 的借用检查器不能理解我们要借用这个 slice 的两个不同
<span class="caption">示例 20-6: 在 `split_at_mut` 函数的实现中使用不安全代码</span>
回忆第四章的 [“Slice 类型” ][the-slice-type] 部分slice 是一个指向一些数据的指针,并带有该 slice 的长度。可以使用 `len` 方法获取 slice 的长度,使用 `as_mut_ptr` 方法访问 slice 的裸指针。在这个例子中,因为有一个 `i32` 值的可变 slice`as_mut_ptr` 返回一个 `*mut i32` 类型的裸指针,储存`ptr` 变量中。
回忆第四章的[“Slice 类型” ][the-slice-type]部分slice 是一个指向一些数据的指针,并带有该 slice 的长度。可以使用 `len` 方法获取 slice 的长度,使用 `as_mut_ptr` 方法访问 slice 的裸指针。在这个例子中,因为有一个 `i32` 值的可变 slice`as_mut_ptr` 返回一个 `*mut i32` 类型的裸指针,并将其存储`ptr` 变量中。
我们保持索引 `mid` 位于 slice 中的断言。接着是不安全代码:`slice::from_raw_parts_mut` 函数获取一个裸指针和一个长度来创建一个 slice。这里使用此函数从 `ptr` 中创建了一个有 `mid` 个项的 slice。之后在 `ptr` 上调用 `add` 方法并使用 `mid` 作为参数来获取一个从 `mid` 开始的裸指针,使用这个裸指针并以 `mid` 之后项的数量为长度创建一个 slice。
我们保持索引 `mid` 位于 slice 中的断言。接着是不安全代码:`slice::from_raw_parts_mut` 函数获取一个裸指针和一个长度来创建一个 slice。这里使用此函数从 `ptr` 中创建了一个有 `mid` 个项的 slice。之后在 `ptr` 上调用 `add` 方法并使用 `mid` 作为参数来获取一个从 `mid` 开始的裸指针,使用这个裸指针并以 `mid` 之后项的数量为长度创建一个 slice。
`slice::from_raw_parts_mut` 函数是不安全的因为它获取一个裸指针,并必须确信这个指针是有效的。裸指针上的 `add` 方法也是不安全的,因为其必须确信此地址偏移量也是有效的指针。因此必须将 `slice::from_raw_parts_mut``add` 放入 `unsafe` 块中以便能调用它们。通过观察代码,和增加 `mid` 必然小于等于 `len` 的断言,我们可以说 `unsafe` 块中所有的裸指针将是有效的 slice 中数据的指针。这是一个可以接受的 `unsafe` 的恰当用法。
注意无需将 `split_at_mut` 函数的结果标记为 `unsafe`,并可以在安全 Rust 中调用此函数。我们创建了一个不安全代码的安全抽象,其代码以一种安全的方式使用了 `unsafe` 代码,因为其只从这个函数访问的数据中创建了有效的指针。
与此相对,示例 20-7 中的 `slice::from_raw_parts_mut` 在使用 slice 时很有可能会崩溃。这段代码获取任意内存地址并创建了一个长为一万的 slice
与此相对,示例 20-7 中的 `slice::from_raw_parts_mut` 在使用 slice 时很有可能会崩溃。这段代码获取任意内存地址并创建了一个长为一万的 slice
```rust
{{#rustdoc_include ../listings/ch20-advanced-features/listing-20-07/src/main.rs:here}}

@ -272,7 +272,7 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
甚至当不涉及 trait 时 newtype 模式也很有用。现在让我们将话题的焦点转移到一些与 Rust 类型系统交互的高级方式上来吧。
[newtype]: ch20-03-advanced-traits.html#newtype-模式用以在外部类型上实现外部-trait
[newtype]: ch20-02-advanced-traits.html#newtype-模式用以在外部类型上实现外部-trait
[implementing-a-trait-on-a-type]: ch10-02-traits.html#为类型实现-trait
[traits-defining-shared-behavior]: ch10-02-traits.html#trait定义共同行为
[smart-pointer-deref]: ch15-02-deref.html#通过实现-deref-trait-将某类型像引用一样处理

@ -169,4 +169,4 @@ ch18-01-what-is-oo.html#封装隐藏了实现细节
ch06-02-match.html#match-控制流结构
[using-trait-objects-that-allow-for-values-of-different-types]:
ch18-02-trait-objects.html#顾及不同类型值的-trait-对象
[using-the-newtype-pattern]: ch20-03-advanced-traits.html#newtype-模式用以在外部类型上实现外部-trait
[using-the-newtype-pattern]: ch20-02-advanced-traits.html#newtype-模式用以在外部类型上实现外部-trait

@ -76,6 +76,6 @@
接下来让我们学习宏!
[advanced-traits]: ch20-03-advanced-traits.html#高级-trait
[advanced-traits]: ch20-02-advanced-traits.html#高级-trait
[enum-values]: ch06-01-defining-an-enum.html#枚举值
[using-trait-objects-that-allow-for-values-of-different-types]: ch18-02-trait-objects.html#顾及不同类型值的-trait-对象

@ -389,7 +389,7 @@ Worker 2 got a job; executing.
示例 20-20 中的代码使用的 `let job = receiver.lock().unwrap().recv().unwrap();` 之所以可以工作是因为对于 `let` 来说,当 `let` 语句结束时任何表达式中等号右侧使用的临时值都会立即被丢弃。然而 `while let``if let` 和 `match`)直到相关的代码块结束都不会丢弃临时值。在示例 20-21 中,`job()` 调用期间锁一直持续,这也意味着其他的 worker 无法接受任务。
[creating-type-synonyms-with-type-aliases]:
ch20-04-advanced-types.html#类型别名用来创建类型同义词
ch20-03-advanced-types.html#类型别名用来创建类型同义词
[integer-types]: ch03-02-data-types.html#整型
[fn-traits]: ch13-01-closures.html#将被捕获的值移出闭包和-fn-trait
[builder]: https://doc.rust-lang.org/std/thread/struct.Builder.html

Loading…
Cancel
Save