mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-14 04:41:49 +08:00
Merge branch 'master' into error_binding
This commit is contained in:
commit
8c8320af3b
@ -33,7 +33,7 @@ let home: IpAddr = "127.0.0.1".parse().unwrap();
|
|||||||
在当有可能会导致有害状态的情况下建议使用 `panic!` —— 在这里,有害状态是指当一些假设、保证、协议或不可变性被打破的状态,例如无效的值、自相矛盾的值或者被传递了不存在的值 —— 外加如下几种情况:
|
在当有可能会导致有害状态的情况下建议使用 `panic!` —— 在这里,有害状态是指当一些假设、保证、协议或不可变性被打破的状态,例如无效的值、自相矛盾的值或者被传递了不存在的值 —— 外加如下几种情况:
|
||||||
|
|
||||||
* 有害状态并不包含 **预期** 会偶尔发生的错误
|
* 有害状态并不包含 **预期** 会偶尔发生的错误
|
||||||
* 之后的代码的运行依赖于处于这种有害状态
|
* 在此之后代码的运行依赖于不处于这种有害状态
|
||||||
* 当没有可行的手段来将有害状态信息编码进所使用的类型中的情况
|
* 当没有可行的手段来将有害状态信息编码进所使用的类型中的情况
|
||||||
|
|
||||||
如果别人调用你的代码并传递了一个没有意义的值,最好的情况也许就是 `panic!` 并警告使用你的库的人他的代码中有 bug 以便他能在开发时就修复它。类似的,`panic!` 通常适合调用不能够控制的外部代码时,这时无法修复其返回的无效状态。
|
如果别人调用你的代码并传递了一个没有意义的值,最好的情况也许就是 `panic!` 并警告使用你的库的人他的代码中有 bug 以便他能在开发时就修复它。类似的,`panic!` 通常适合调用不能够控制的外部代码时,这时无法修复其返回的无效状态。
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
> [ch15-06-reference-cycles.md](https://github.com/rust-lang/book/blob/master/src/ch15-06-reference-cycles.md) > <br>
|
> [ch15-06-reference-cycles.md](https://github.com/rust-lang/book/blob/master/src/ch15-06-reference-cycles.md) > <br>
|
||||||
> commit f617d58c1a88dd2912739a041fd4725d127bf9fb
|
> commit f617d58c1a88dd2912739a041fd4725d127bf9fb
|
||||||
|
|
||||||
Rust 的内存安全性保证使其难以意外地制造永远也不会被清理的内存(被称为 **内存泄露**(_memory leak_)),但并不是不可能。与在编译时拒绝数据竞争不同, Rust 并不保证完全地避免内存泄露,这意味着内存泄露在 Rust 被认为是内存安全的。这一点可以通过 `Rc<T>` 和 `RefCell<T>` 看出:创建引用循环的可能性是存在的。这会造成内存泄露,因为每一项的引用计数永远也到不了 0,其值也永远也不会被丢弃。
|
Rust 的内存安全性保证使其难以意外地制造永远也不会被清理的内存(被称为 **内存泄露**(_memory leak_)),但并不是不可能。与在编译时拒绝数据竞争不同, Rust 并不保证完全地避免内存泄露,这意味着内存泄露在 Rust 被认为是内存安全的。这一点可以通过 `Rc<T>` 和 `RefCell<T>` 看出:创建引用循环的可能性是存在的。这会造成内存泄露,因为每一项的引用计数永远也到不了 0,其值也永远不会被丢弃。
|
||||||
|
|
||||||
### 制造引用循环
|
### 制造引用循环
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
> [ch16-01-threads.md](https://github.com/rust-lang/book/blob/master/src/ch16-01-threads.md) > <br>
|
> [ch16-01-threads.md](https://github.com/rust-lang/book/blob/master/src/ch16-01-threads.md) > <br>
|
||||||
> commit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f
|
> commit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f
|
||||||
|
|
||||||
在大部分现代操作系统中,执行中程序的代码在一个 **进程**(_process_)中运行,操作系统则负责管理多个进程。在程序内部,也可以拥有多个同时运行的独立部分。这个运行这些独立部分的功能被称为 **线程**(_threads_)。
|
在大部分现代操作系统中,已执行程序的代码在一个 **进程**(_process_)中运行,操作系统则负责管理多个进程。在程序内部,也可以拥有多个同时运行的独立部分。运行这些独立部分的功能被称为 **线程**(_threads_)。
|
||||||
|
|
||||||
将程序中的计算拆分进多个线程可以改善性能,因为程序可以同时进行多个任务,不过这也会增加复杂性。因为线程是同时运行的,所以无法预先保证不同线程中的代码的执行顺序。这会导致诸如此类的问题:
|
将程序中的计算拆分进多个线程可以改善性能,因为程序可以同时进行多个任务,不过这也会增加复杂性。因为线程是同时运行的,所以无法预先保证不同线程中的代码的执行顺序。这会导致诸如此类的问题:
|
||||||
|
|
||||||
@ -11,7 +11,7 @@
|
|||||||
- 死锁(Deadlocks),两个线程相互等待对方停止使用其所拥有的资源,这会阻止它们继续运行
|
- 死锁(Deadlocks),两个线程相互等待对方停止使用其所拥有的资源,这会阻止它们继续运行
|
||||||
- 只会发生在特定情况且难以稳定重现和修复的 bug
|
- 只会发生在特定情况且难以稳定重现和修复的 bug
|
||||||
|
|
||||||
Rust 尝试缓和使用线程的负面影响。不过在多线程上下文中编程仍需格外小心,同时其所要求的代码结构也不同于运行于单线程的程序。
|
Rust 尝试减轻使用线程的负面影响。不过在多线程上下文中编程仍需格外小心,同时其所要求的代码结构也不同于运行于单线程的程序。
|
||||||
|
|
||||||
编程语言有一些不同的方法来实现线程。很多操作系统提供了创建新线程的 API。这种由编程语言调用操作系统 API 创建线程的模型有时被称为 _1:1_,一个 OS 线程对应一个语言线程。
|
编程语言有一些不同的方法来实现线程。很多操作系统提供了创建新线程的 API。这种由编程语言调用操作系统 API 创建线程的模型有时被称为 _1:1_,一个 OS 线程对应一个语言线程。
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ Rust 尝试缓和使用线程的负面影响。不过在多线程上下文中编
|
|||||||
|
|
||||||
在当前上下文中,**运行时** 代表二进制文件中包含的由语言自身提供的代码。这些代码根据语言的不同可大可小,不过任何非汇编语言都会有一定数量的运行时代码。为此,通常人们说一个语言 “没有运行时”,一般意味着 “小运行时”。更小的运行时拥有更少的功能不过其优势在于更小的二进制输出,这使其易于在更多上下文中与其他语言相结合。虽然很多语言觉得增加运行时来换取更多功能没有什么问题,但是 Rust 需要做到几乎没有运行时,同时为了保持高性能必需能够调用 C 语言,这点也是不能妥协的。
|
在当前上下文中,**运行时** 代表二进制文件中包含的由语言自身提供的代码。这些代码根据语言的不同可大可小,不过任何非汇编语言都会有一定数量的运行时代码。为此,通常人们说一个语言 “没有运行时”,一般意味着 “小运行时”。更小的运行时拥有更少的功能不过其优势在于更小的二进制输出,这使其易于在更多上下文中与其他语言相结合。虽然很多语言觉得增加运行时来换取更多功能没有什么问题,但是 Rust 需要做到几乎没有运行时,同时为了保持高性能必需能够调用 C 语言,这点也是不能妥协的。
|
||||||
|
|
||||||
绿色线程的 M:N 模型需要更大的语言运行时来管理这些线程。因此,Rust 标准库只提供了 1:1 线程模型实现。由于 Rust 是较为底层的语言,如果你愿意牺牲性能来换取的抽象,以获得对线程运行更精细的控制及更低的上下文切换成本,你可以使用实现了 M:N 线程模型的 crate。
|
绿色线程的 M:N 模型需要更大的语言运行时来管理这些线程。因此,Rust 标准库只提供了 1:1 线程模型实现。由于 Rust 是较为底层的语言,如果你愿意牺牲性能来换取抽象,以获得对线程运行更精细的控制及更低的上下文切换成本,你可以使用实现了 M:N 线程模型的 crate。
|
||||||
|
|
||||||
现在我们明白了 Rust 中的线程是如何定义的,让我们开始探索如何使用标准库提供的线程相关的 API 吧。
|
现在我们明白了 Rust 中的线程是如何定义的,让我们开始探索如何使用标准库提供的线程相关的 API 吧。
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ hi number 5 from the spawned thread!
|
|||||||
|
|
||||||
由于主线程结束,示例 16-1 中的代码大部分时候不光会提早结束新建线程,甚至不能实际保证新建线程会被执行。其原因在于无法保证线程运行的顺序!
|
由于主线程结束,示例 16-1 中的代码大部分时候不光会提早结束新建线程,甚至不能实际保证新建线程会被执行。其原因在于无法保证线程运行的顺序!
|
||||||
|
|
||||||
可以通过将 `thread::spawn` 的返回值储存在变量中来修复新建线程部分没有执行或者完全没有执行的问题。`thread::spawn` 的返回值类型是 `JoinHandle`。`JoinHandle` 是一个拥有所有权的值,当对其调用 `join` 方法时,它会等待其线程结束。示例 16-2 展示了如何使用示例 16-1 这个中创建的线程的 `JoinHandle` 并调用 `join` 来确保新建线程在 `main` 退出前结束运行:
|
可以通过将 `thread::spawn` 的返回值储存在变量中来修复新建线程部分没有执行或者完全没有执行的问题。`thread::spawn` 的返回值类型是 `JoinHandle`。`JoinHandle` 是一个拥有所有权的值,当对其调用 `join` 方法时,它会等待其线程结束。示例 16-2 展示了如何使用示例 16-1 中创建的线程的 `JoinHandle` 并调用 `join` 来确保新建线程在 `main` 退出前结束运行:
|
||||||
|
|
||||||
<span class="filename">文件名: src/main.rs</span>
|
<span class="filename">文件名: src/main.rs</span>
|
||||||
|
|
||||||
@ -285,4 +285,4 @@ error[E0382]: use of moved value: `v`
|
|||||||
|
|
||||||
Rust 的所有权规则又一次帮助了我们!示例 16-3 中的错误是因为 Rust 是保守的并只会为线程借用 `v`,这意味着主线程理论上可能使新建线程的引用无效。通过告诉 Rust 将 `v` 的所有权移动到新建线程,我们向 Rust 保证主线程不会再使用 `v`。如果对示例 16-4 也做出如此修改,那么当在主线程中使用 `v` 时就会违反所有权规则。 `move` 关键字覆盖了 Rust 默认保守的借用:其也不允许我们违反所有权规则。
|
Rust 的所有权规则又一次帮助了我们!示例 16-3 中的错误是因为 Rust 是保守的并只会为线程借用 `v`,这意味着主线程理论上可能使新建线程的引用无效。通过告诉 Rust 将 `v` 的所有权移动到新建线程,我们向 Rust 保证主线程不会再使用 `v`。如果对示例 16-4 也做出如此修改,那么当在主线程中使用 `v` 时就会违反所有权规则。 `move` 关键字覆盖了 Rust 默认保守的借用:其也不允许我们违反所有权规则。
|
||||||
|
|
||||||
现在我们有一个线程和线程 API 的基本了解,让我们讨论一下使用线程实际可以 **做** 什么吧。
|
现在我们对线程和线程 API 有了基本的了解,让我们讨论一下使用线程实际可以 **做** 什么吧。
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
> [ch16-02-message-passing.md](https://github.com/rust-lang/book/blob/master/src/ch16-02-message-passing.md) > <br>
|
> [ch16-02-message-passing.md](https://github.com/rust-lang/book/blob/master/src/ch16-02-message-passing.md) > <br>
|
||||||
> commit 26565efc3f62d9dacb7c2c6d0f5974360e459493
|
> commit 26565efc3f62d9dacb7c2c6d0f5974360e459493
|
||||||
|
|
||||||
一个日益流行的确保安全并发的方式是 **消息传递**(_message passing_),这里线程或 actor 通过发送包含数据的消息来相互沟通。这个思想来源于 [Go 编程语言文档中](http://golang.org/doc/effective_go.html) 的口号:“不要共享内存来通讯;而是要通讯来共享内存。”(“Do not communicate by sharing memory; instead, share memory by communicating.”)
|
一个日益流行的确保安全并发的方式是 **消息传递**(_message passing_),这里线程或 actor 通过发送包含数据的消息来相互沟通。这个思想来源于 [Go 编程语言文档中](http://golang.org/doc/effective_go.html) 的口号:“不要通过共享内存来通讯;而是通过通讯来共享内存。”(“Do not communicate by sharing memory; instead, share memory by communicating.”)
|
||||||
|
|
||||||
Rust 中一个实现消息传递并发的主要工具是 **通道**(_channel_),一个 Rust 标准库提供了其实现的编程概念。你可以将其想象为一个水流的通道,比如河流或小溪。如果你将诸如橡皮鸭或小船之类的东西放入其中,它们会顺流而下到达下游。
|
Rust 中一个实现消息传递并发的主要工具是 **通道**(_channel_),Rust 标准库提供了其实现的编程概念。你可以将其想象为一个水流的通道,比如河流或小溪。如果你将诸如橡皮鸭或小船之类的东西放入其中,它们会顺流而下到达下游。
|
||||||
|
|
||||||
编程中的通道有两部分组成,一个发送者(transmitter)和一个接收者(receiver)。发送者位于上游位置,在这里可以将橡皮鸭放入河中,接收者部分则位于下游,橡皮鸭最终会漂流至此。代码中的一部分调用发送者的方法以及希望发送的数据,另一部分则检查接收端收到的消息。当发送者或接收者任一被丢弃时可以认为通道被 **关闭**(_closed_)了。
|
编程中的通道有两部分组成,一个发送者(transmitter)和一个接收者(receiver)。发送者位于上游位置,在这里可以将橡皮鸭放入河中,接收者则位于下游,橡皮鸭最终会漂流至此。代码中的一部分调用发送者的方法以及希望发送的数据,另一部分则检查接收端收到的消息。当发送者或接收者任一被丢弃时可以认为通道被 **关闭**(_closed_)了。
|
||||||
|
|
||||||
这里,我们将开发一个程序,它会在一个线程生成值向通道发送,而在另一个线程会接收值并打印出来。这里会通过通道在线程间发送简单值来演示这个功能。一旦你熟悉了这项技术,就能使用通道来实现聊天系统,或利用很多线程进行分布式计算并将部分计算结果发送给一个线程进行聚合。
|
这里,我们将开发一个程序,它会在一个线程生成值向通道发送,而在另一个线程会接收值并打印出来。这里会通过通道在线程间发送简单值来演示这个功能。一旦你熟悉了这项技术,就能使用通道来实现聊天系统,或利用很多线程进行分布式计算并将部分计算结果发送给一个线程进行聚合。
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ fn main() {
|
|||||||
|
|
||||||
<span class="caption">示例 16-6: 创建一个通道,并将其两端赋值给 `tx` 和 `rx`</span>
|
<span class="caption">示例 16-6: 创建一个通道,并将其两端赋值给 `tx` 和 `rx`</span>
|
||||||
|
|
||||||
这里使用 `mpsc::channel` 函数创建一个新的通道;`mpsc` 是 **多个生产者,单个消费者**(_multiple producer, single consumer_)的缩写。简而言之,Rust 标准库实现通道的方式意味着一个通道可以有多个产生值的 **发送**(_sending_)端,但只能有一个消费这些值的 **接收**(_receiving_)端。想象一下多条小河小溪最终汇聚成大河:所有通过这些小河发出的东西最后都会来到大河的下游。目前我们以单个生产者开始,但是当示例可以工作后会增加多个生产者。
|
这里使用 `mpsc::channel` 函数创建一个新的通道;`mpsc` 是 **多个生产者,单个消费者**(_multiple producer, single consumer_)的缩写。简而言之,Rust 标准库实现通道的方式意味着一个通道可以有多个产生值的 **发送**(_sending_)端,但只能有一个消费这些值的 **接收**(_receiving_)端。想象一下多条小河小溪最终汇聚成大河:所有通过这些小河发出的东西最后都会来到下游的大河。目前我们以单个生产者开始,但是当示例可以工作后会增加多个生产者。
|
||||||
|
|
||||||
`mpsc::channel` 函数返回一个元组:第一个元素是发送端,而第二个元素是接收端。由于历史原因,`tx` 和 `rx` 通常作为 **发送者**(_transmitter_)和 **接收者**(_receiver_)的缩写,所以这就是我们将用来绑定这两端变量的名字。这里使用了一个 `let` 语句和模式来解构了此元组;第十八章会讨论 `let` 语句中的模式和解构。如此使用 `let` 语句是一个方便提取 `mpsc::channel` 返回的元组中一部分的手段。
|
`mpsc::channel` 函数返回一个元组:第一个元素是发送端,而第二个元素是接收端。由于历史原因,`tx` 和 `rx` 通常作为 **发送者**(_transmitter_)和 **接收者**(_receiver_)的缩写,所以这就是我们将用来绑定这两端变量的名字。这里使用了一个 `let` 语句和模式来解构了此元组;第十八章会讨论 `let` 语句中的模式和解构。如此使用 `let` 语句是一个方便提取 `mpsc::channel` 返回的元组中一部分的手段。
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ if let Some(x) = some_option_value {
|
|||||||
|
|
||||||
<span class="caption">示例 18-9: 使用 `if let` 和一个带有可反驳模式的代码块来代替 `let`</span>
|
<span class="caption">示例 18-9: 使用 `if let` 和一个带有可反驳模式的代码块来代替 `let`</span>
|
||||||
|
|
||||||
我们给了代码一个得以继续的出路!这段代码可以完美运行,当让如此意味着我们不能再使用不可反驳模式并免于收到错误。如果为 `if let` 提供了一个总是会匹配的模式,比如示例 18-10 中的 `x`,编译器会给出一个警告:
|
我们给了代码一个得以继续的出路!这段代码可以完美运行,尽管这意味着我们不能再使用不可反驳模式并免于收到错误。如果为 `if let` 提供了一个总是会匹配的模式,比如示例 18-10 中的 `x`,编译器会给出一个警告:
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
if let x = 5 {
|
if let x = 5 {
|
||||||
@ -65,6 +65,6 @@ warning: irrefutable if-let pattern
|
|||||||
= note: #[warn(irrefutable_let_patterns)] on by default
|
= note: #[warn(irrefutable_let_patterns)] on by default
|
||||||
```
|
```
|
||||||
|
|
||||||
如此,匹配分支必须使用可反驳模式,除了最后一个分支需要使用能匹配任何剩余值的不可反驳模式。允许将不可反驳模式用于只有一个分支的 `match`,不过这么做不是特别有用,并可以被更简单的 `let` 语句替代。
|
基于此,`match`匹配分支必须使用可反驳模式,除了最后一个分支需要使用能匹配任何剩余值的不可反驳模式。Rust允许我们在只有一个匹配分支的`match`中使用不可反驳模式,不过这么做不是特别有用,并可以被更简单的 `let` 语句替代。
|
||||||
|
|
||||||
目前我们已经讨论了所有可以使用模式的地方, 以及可反驳模式与不可反驳模式的区别,下面让我们一起去把可以用来创建模式的语法过目一遍吧。
|
目前我们已经讨论了所有可以使用模式的地方, 以及可反驳模式与不可反驳模式的区别,下面让我们一起去把可以用来创建模式的语法过目一遍吧。
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
> <br>
|
> <br>
|
||||||
> commit 86f0ae4831f24b3c429fa4845b900b4cad903a8b
|
> commit 86f0ae4831f24b3c429fa4845b900b4cad903a8b
|
||||||
|
|
||||||
通过本书我们已领略过许多不同类型模式的例子。本节会统一列出所有在模式中有效的语法并且会阐述你为什么可能会希望使用其中的每一个语法。
|
通过本书我们已领略过许多不同类型模式的例子。在本节中,我们收集了模式中所有有效的语法,并讨论了为什么可能要使用每个语法。
|
||||||
|
|
||||||
### 匹配字面值
|
### 匹配字面值
|
||||||
|
|
||||||
@ -21,7 +21,7 @@ match x {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
这段代码会打印 `one` 因为 `x` 的值是 1。这个语法用于代码得到某个具体值时进行操作。
|
这段代码会打印 `one` 因为 `x` 的值是 1。如果希望代码获得特定的具体值,则该语法很有用。
|
||||||
|
|
||||||
### 匹配命名变量
|
### 匹配命名变量
|
||||||
|
|
||||||
@ -235,7 +235,7 @@ fn main() {
|
|||||||
|
|
||||||
对于像 `Message::Move` 这样的类结构体枚举成员,可以采用类似于匹配结构体的模式。在成员名称后,使用大括号并列出字段变量以便将其分解以供此分支的代码使用。这里使用了示例 18-13 所展示的简写。
|
对于像 `Message::Move` 这样的类结构体枚举成员,可以采用类似于匹配结构体的模式。在成员名称后,使用大括号并列出字段变量以便将其分解以供此分支的代码使用。这里使用了示例 18-13 所展示的简写。
|
||||||
|
|
||||||
对于像 `Message::Write` 这样的包含一个元素,以及像 `Message::ChangeColor` 这样包含两个元素的类元组枚举成员,其模式则类似于用于解构元组的模式。模式中变量的数量必须与成员中元素的数量一致。
|
对于像 `Message::Write` 这样的包含一个元素,以及像 `Message::ChangeColor` 这样包含三个元素的类元组枚举成员,其模式则类似于用于解构元组的模式。模式中变量的数量必须与成员中元素的数量一致。
|
||||||
|
|
||||||
#### 解构嵌套的结构体和枚举
|
#### 解构嵌套的结构体和枚举
|
||||||
|
|
||||||
|
@ -12,6 +12,6 @@
|
|||||||
* 高级 trait:与 trait 相关的关联类型,默认类型参数,完全限定语法(fully qualified syntax),超(父)trait(supertraits)和 newtype 模式
|
* 高级 trait:与 trait 相关的关联类型,默认类型参数,完全限定语法(fully qualified syntax),超(父)trait(supertraits)和 newtype 模式
|
||||||
* 高级类型:关于 newtype 模式的更多内容,类型别名,never 类型和动态大小类型
|
* 高级类型:关于 newtype 模式的更多内容,类型别名,never 类型和动态大小类型
|
||||||
* 高级函数和闭包:函数指针和返回闭包
|
* 高级函数和闭包:函数指针和返回闭包
|
||||||
* 宏:定义在编译时定义更多更多代码的方式
|
* 宏:定义在编译时定义更多代码的方式
|
||||||
|
|
||||||
对所有人而言,这都是一个介绍 Rust 迷人特性的宝典!让我们翻开它吧!
|
对所有人而言,这都是一个介绍 Rust 迷人特性的宝典!让我们翻开它吧!
|
||||||
|
@ -91,7 +91,7 @@ unsafe {
|
|||||||
|
|
||||||
### 调用不安全函数或方法
|
### 调用不安全函数或方法
|
||||||
|
|
||||||
第二类要求使用不安全块的操作是调用不安全函数。不安全函数和方法与常规函数方法十分类似,除了其开头有一个额外的 `unsafe`。`unsafe` 表明我们作为程序需要满足其要求,因为 Rust 不会保证满足这些要求。通过在 `unsafe` 块中调用不安全函数,我们表明已经阅读过此函数的文档并对其是否满足函数自身的契约负责。
|
第二类要求使用不安全块的操作是调用不安全函数。不安全函数和方法与常规函数方法十分类似,除了其开头有一个额外的 `unsafe`。在此上下文中,关键字`unsafe`表示该函数具有调用时需要满足的要求,而 Rust 不会保证满足这些要求。通过在 `unsafe` 块中调用不安全函数,表明我们已经阅读过此函数的文档并对其是否满足函数自身的契约负责。
|
||||||
|
|
||||||
如下是一个没有做任何操作的不安全函数 `dangerous` 的例子:
|
如下是一个没有做任何操作的不安全函数 `dangerous` 的例子:
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user