pull/182/head
KaiserY 7 years ago
commit f3d31da5d8

@ -150,7 +150,7 @@ io::stdin().read_line(&mut guess)
.expect("Failed to read line"); .expect("Failed to read line");
``` ```
如果程序的开头没有 `use std::io` 这一行,可以把函数调用写成 `std::io::stdin`。`stdin` 函数返回一个 [`std::io::stdin`][iostdin]<!-- ignore --> 的实例,这代表终端标准输入句柄的类型。 如果程序的开头没有 `use std::io` 这一行,可以把函数调用写成 `std::io::stdin`。`stdin` 函数返回一个 [`std::io::Stdin`][iostdin]<!-- ignore --> 的实例,这代表终端标准输入句柄的类型。
[iostdin]: https://doc.rust-lang.org/std/io/struct.Stdin.html [iostdin]: https://doc.rust-lang.org/std/io/struct.Stdin.html

@ -28,7 +28,7 @@ fn main() {
### 使用 `use` 关键字将名称导入作用域 ### 使用 `use` 关键字将名称导入作用域
Rust 的 `use` 关键字的工作通过将想要调用的函数所在的模块引入到作用域中来缩短冗长的函数调用。这是一个将 `a::series::of` 模块导入一个二进制 crate 的根作用域的例子: Rust 的 `use` 关键字能将你想要调用的函数所在的模块引入到当前作用域中,通过这种方式可以缩短冗长的函数调用。这是一个将 `a::series::of` 模块导入一个二进制 crate 的根作用域的例子:
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>

@ -59,7 +59,7 @@ RefCell<T> is, perhaps a succinct round up would help? -->
- `Box<T>` 允许在编译时执行不可变(或可变)借用检查;`Rc<T>`仅允许在编译时执行不可变借用检查;`RefCell<T>` 允许在运行时执行不可变(或可变)借用检查。 - `Box<T>` 允许在编译时执行不可变(或可变)借用检查;`Rc<T>`仅允许在编译时执行不可变借用检查;`RefCell<T>` 允许在运行时执行不可变(或可变)借用检查。
- 因为 `RefCell<T>` 允许在运行时执行可变借用检查,所以我们可以在即便 `RefCell<T>` 自身是不可变的情况下修改其内部的值。 - 因为 `RefCell<T>` 允许在运行时执行可变借用检查,所以我们可以在即便 `RefCell<T>` 自身是不可变的情况下修改其内部的值。
一个理由便是指 **内部可变性** 模式。让我们看看何时内部可变性是有用的,并讨论这是如何成为可能的。 一个理由便是指 **内部可变性** 模式。让我们看看何时内部可变性是有用的,并讨论这是如何成为可能的。
### 内部可变性:不可变值的可变借用 ### 内部可变性:不可变值的可变借用

@ -243,7 +243,7 @@ fn main() {
为了使子结点知道其父结点,需要在 `Node` 结构体定义中增加一个 `parent` 字段。问题是 `parent` 的类型应该是什么。我们知道其不能包含 `Rc<T>`,因为这样 `leaf.parent` 将会指向 `branch``branch.children` 会包含 `leaf` 的指针,这会形成引用循环,会造成其 `strong_count` 永远也不会为 0. 为了使子结点知道其父结点,需要在 `Node` 结构体定义中增加一个 `parent` 字段。问题是 `parent` 的类型应该是什么。我们知道其不能包含 `Rc<T>`,因为这样 `leaf.parent` 将会指向 `branch``branch.children` 会包含 `leaf` 的指针,这会形成引用循环,会造成其 `strong_count` 永远也不会为 0.
现在换一种方式思考这个关系,父结点应该拥有其子结点:如果父结点被丢弃了,其子结点也应该丢弃。然而子结点不应该拥有其父结点:如果丢弃子结点,其父结点应该依然存在。这正是弱引用的例子! 现在换一种方式思考这个关系,父结点应该拥有其子结点:如果父结点被丢弃了,其子结点也应该丢弃。然而子结点不应该拥有其父结点:如果丢弃子结点,其父结点应该依然存在。这正是弱引用的例子!
所以 `parent` 使用 `Weak<T>` 类型而不是 `Rc`,具体来说是 `RefCell<Weak<Node>>`。现在 `Node` 结构体定义看起来像这样: 所以 `parent` 使用 `Weak<T>` 类型而不是 `Rc`,具体来说是 `RefCell<Weak<Node>>`。现在 `Node` 结构体定义看起来像这样:
@ -423,7 +423,7 @@ like that to the start of the Weak section? -->
这一章涵盖了如何使用智能指针来做出不同于 Rust 常规引用默认所提供的保证与取舍。`Box<T>` 有一个已知的大小并指向分配在堆上的数据。`Rc<T>` 记录了堆上数据的引用数量以便可以拥有多个所有者。`RefCell<T>` 和其内部可变性提供了一个可以用于当需要不可变类型但是需要改变其内部值能力的类型,并在运行时而不是编译时检查借用规则。 这一章涵盖了如何使用智能指针来做出不同于 Rust 常规引用默认所提供的保证与取舍。`Box<T>` 有一个已知的大小并指向分配在堆上的数据。`Rc<T>` 记录了堆上数据的引用数量以便可以拥有多个所有者。`RefCell<T>` 和其内部可变性提供了一个可以用于当需要不可变类型但是需要改变其内部值能力的类型,并在运行时而不是编译时检查借用规则。
我们还介绍了提供了很多智能指针功能的 trait `Deref``Drop`。同时探索了会造成内存泄露的引用虚幻,以及如何使用 `Weak<T>` 来避免它们。 我们还介绍了提供了很多智能指针功能的 trait `Deref``Drop`。同时探索了会造成内存泄露的引用循环,以及如何使用 `Weak<T>` 来避免它们。
如果本章内容引起了你的兴趣并希望现在就实现你自己的智能指针的话,请阅读 [“The Nomicon”] 来获取更多有用的信息。 如果本章内容引起了你的兴趣并希望现在就实现你自己的智能指针的话,请阅读 [“The Nomicon”] 来获取更多有用的信息。

@ -6,7 +6,7 @@
安全并高效的处理并发编程是 Rust 的另一个主要目标。**并发编程***Concurrent programming*),代表程序的不同部分相互独立的执行,而 **并行编程***parallel programming*代表程序不同部分于同时执行这两个概念随着计算机越来越多的利用多处理器的优势时显得愈发重要。由于历史原因在此类上下文中编程一直是困难且容易出错的Rust 希望能改变这一点。 安全并高效的处理并发编程是 Rust 的另一个主要目标。**并发编程***Concurrent programming*),代表程序的不同部分相互独立的执行,而 **并行编程***parallel programming*代表程序不同部分于同时执行这两个概念随着计算机越来越多的利用多处理器的优势时显得愈发重要。由于历史原因在此类上下文中编程一直是困难且容易出错的Rust 希望能改变这一点。
起初Rust 团队认为确保内存安全和防止并发问题是两个分别需要不同方法应对的挑战。随着时间的推移,团队发现所有权和类型系统是一系列解决内存安全 **和** 并发问题的强力的工具通过改进所有权和类型检查Rust 很多并发错误都是 **编译时** 错误,而非运行时错误。因此,相比花费大量时间尝试重现运行时并发 bug 出现的特定情况Rust 会拒绝编译不正确的代码并提供解释问题的错误信息。因此,你可以在开发时而不是不慎部署到生产环境后修复代码。我们给 Rust 的这一部分起了一个绰号 **无畏并发***fearless concurrency*)。无畏并发令你的代码免于出现诡异的 bug 并可以轻松重构且无需担心会引入新的 bug。 起初Rust 团队认为确保内存安全和防止并发问题是两个分别需要不同方法应对的挑战。随着时间的推移,团队发现所有权和类型系统是一系列解决内存安全 **和** 并发问题的强力的工具通过改进所有权和类型检查Rust 很多并发错误都是 **编译时** 错误,而非运行时错误。因此,相比花费大量时间尝试重现运行时并发 bug 出现的特定情况Rust 会拒绝编译不正确的代码并提供解释问题的错误信息。因此,你可以在开发时而不是不慎部署到生产环境后修复代码。我们给 Rust 的这一部分起了一个绰号 **无畏并发***fearless concurrency*)。无畏并发令你的代码免于出现诡异的 bug 并可以轻松重构且无需担心会引入新的 bug。
> 注意:出于简洁的考虑,我们将很多问题归为并发,而不是更准确的区分并发和(或)并行。如果这是一本专注于并发和/或并行的书,我们肯定会更加精确的。对于本章,当我们谈到并发时,请自行脑内替换为并发和(或)并行。 > 注意:出于简洁的考虑,我们将很多问题归为并发,而不是更准确的区分并发和(或)并行。如果这是一本专注于并发和/或并行的书,我们肯定会更加精确的。对于本章,当我们谈到并发时,请自行脑内替换为并发和(或)并行。

@ -20,7 +20,7 @@ Rust 尝试缓和使用线程的负面影响。不过在多线程上下文中编
每一个模型都有其优势和取舍。对于 Rust 来说最重要的取舍是运行时支持。运行时是一个令人迷惑的概念,其在不同上下文中可能有不同的含义。 每一个模型都有其优势和取舍。对于 Rust 来说最重要的取舍是运行时支持。运行时是一个令人迷惑的概念,其在不同上下文中可能有不同的含义。
在当前上下文中,**运行时** 代表进制文件中包含的由语言自身提供的代码。这些代码根据语言的不同可大可小,不过任何非汇编语言都会有一定数量的运行时代码。为此,通常人们说一个语言 “没有运行时”,一般意味着 “小运行时”。更小的运行时拥有更少的功能不过其优势在于更小的二进制输出,这使其易于在更多上下文中与其他语言结合。虽然很多语言觉得增加运行时来换取更多功能没有什么问题,但是 Rust 需要做到几乎没有运行时,同时为了保持高性能必需能够调用 C 语言,这点也是不能妥协的。 在当前上下文中,**运行时** 代表进制文件中包含的由语言自身提供的代码。这些代码根据语言的不同可大可小,不过任何非汇编语言都会有一定数量的运行时代码。为此,通常人们说一个语言 “没有运行时”,一般意味着 “小运行时”。更小的运行时拥有更少的功能不过其优势在于更小的二进制输出,这使其易于在更多上下文中与其他语言结合。虽然很多语言觉得增加运行时来换取更多功能没有什么问题,但是 Rust 需要做到几乎没有运行时,同时为了保持高性能必需能够调用 C 语言,这点也是不能妥协的。
绿色线程的 M:N 模型更大的语言运行时来管理这些线程。为此Rust 标准库只提供了 1:1 线程模型实现。因为 Rust 是如此底层的语言,所以有相应的 crate 实现了 M:N 线程模型,如果你宁愿牺牲性能来换取例如更好的线程运行控制和更低的上下文切换成本。 绿色线程的 M:N 模型更大的语言运行时来管理这些线程。为此Rust 标准库只提供了 1:1 线程模型实现。因为 Rust 是如此底层的语言,所以有相应的 crate 实现了 M:N 线程模型,如果你宁愿牺牲性能来换取例如更好的线程运行控制和更低的上下文切换成本。

@ -58,7 +58,7 @@ fn main() {
让我们看看当 `match` 语句运行的时候发生了什么。第一个匹配分支的模式并不匹配 `x` 中定义的值,所以继续。 让我们看看当 `match` 语句运行的时候发生了什么。第一个匹配分支的模式并不匹配 `x` 中定义的值,所以继续。
第二个匹配分支中的模式引入了一个新变量 `y`,它会匹配任何 `Some` 中的值。因为我们在 `match` 表达式的新作用域中,这是一个新变量,而不是开头声明为值 10 的那个 `y`。这个新的 `y` 绑定会撇配任何 `Some` 中的值,在这是`x` 中的值。因此这个 `y` 绑定了 `x``Some` 内部的值。这个值是 5所以这个分支的表达式将会执行并打印出 `Matched, y = 5` 第二个匹配分支中的模式引入了一个新变量 `y`,它会匹配任何 `Some` 中的值。因为我们在 `match` 表达式的新作用域中,这是一个新变量,而不是开头声明为值 10 的那个 `y`。这个新的 `y` 绑定会匹配任何 `Some` 中的值,在这里`x` 中的值。因此这个 `y` 绑定了 `x``Some` 内部的值。这个值是 5所以这个分支的表达式将会执行并打印出 `Matched, y = 5`
<!-- Below -- We haven't fully introduced the underscore yet, is there anything <!-- Below -- We haven't fully introduced the underscore yet, is there anything
else we could use for that final arm? --> else we could use for that final arm? -->
@ -238,7 +238,7 @@ fn main() {
#### 解构枚举 #### 解构枚举
本书之前的部分曾经解构过么局,比如第六章中示例 6-5 中解构了一个 `Option<i32>`。一个当时没有明确提到的细节是解构枚举的模式需要对应枚举所定义的储存数据的方式。让我们以示例 6-2 中的 `Message` 枚举为例,编写一个 `match` 使用模式解构每一个内部值,如示例 18-15 所示: 本书之前的部分曾经解构过枚举,比如第六章中示例 6-5 中解构了一个 `Option<i32>`。一个当时没有明确提到的细节是解构枚举的模式需要对应枚举所定义的储存数据的方式。让我们以示例 6-2 中的 `Message` 枚举为例,编写一个 `match` 使用模式解构每一个内部值,如示例 18-15 所示:
<span class="filename">文件名: src/main.rs</span> <span class="filename">文件名: src/main.rs</span>

@ -356,4 +356,4 @@ unsafe impl Foo for i32 {
### 何时使用不安全代码 ### 何时使用不安全代码
使用 `unsafe` 来进行这四个操作之一是没有问题的,甚至是不需要深思熟虑的,不过使得 `unsafe` 代码正确也实属不易因为编译器不能帮助保证内存安全。当有理由使用 `unsafe` 代码时,是可以么做的,通过使用显式的 `unsafe` 标注使得在出现错误时易于追踪问题的源头。 使用 `unsafe` 来进行这四个操作之一是没有问题的,甚至是不需要深思熟虑的,不过使得 `unsafe` 代码正确也实属不易因为编译器不能帮助保证内存安全。当有理由使用 `unsafe` 代码时,是可以么做的,通过使用显式的 `unsafe` 标注使得在出现错误时易于追踪问题的源头。

@ -218,7 +218,7 @@ Rust 需要知道应该为特定类型的值分配多少内存,同时所有同
所以虽然 `&T` 是一个储存了 `T` 所在的内存位置的单个值,`&str` 则是 **两个** 值:`str` 的地址和其长度。这样,`&str` 就有了一个在编译时可以知道的大小:它是 `usize` 长度的两倍。也就是说,我们总是知道 `&str` 的大小,而无论其引用的字符串是多长。这里是 Rust 中动态大小类型的常规用法:他们有一些额外的元信息来储存动态信息的大小。这引出了动态大小类型的黄金规则:必须将动态大小类型的值置于某种指针之后。 所以虽然 `&T` 是一个储存了 `T` 所在的内存位置的单个值,`&str` 则是 **两个** 值:`str` 的地址和其长度。这样,`&str` 就有了一个在编译时可以知道的大小:它是 `usize` 长度的两倍。也就是说,我们总是知道 `&str` 的大小,而无论其引用的字符串是多长。这里是 Rust 中动态大小类型的常规用法:他们有一些额外的元信息来储存动态信息的大小。这引出了动态大小类型的黄金规则:必须将动态大小类型的值置于某种指针之后。
可以将 `str` 与所有类型的指针结合:比如 `Box<str>``Rc<str>`。事实上之前我们已经见过了不过是另一个动态大小类型trait。每一个 trait 都是一个可以通过 trait 名称来引用的动态大小类型。在第十七章 “为使用不同类型的值而设计的 trait 对象” 部分,我们提到了为了将 trait 用于 trait 对象,必须将他们放入指针之后,比如 `&Trait``Box<Trait>``Rc<Trait>` 也可以。trait 之所以是动态大小类型的原因是必须这样才能这样使用它。 可以将 `str` 与所有类型的指针结合:比如 `Box<str>``Rc<str>`。事实上之前我们已经见过了不过是另一个动态大小类型trait。每一个 trait 都是一个可以通过 trait 名称来引用的动态大小类型。在第十七章 “为使用不同类型的值而设计的 trait 对象” 部分,我们提到了为了将 trait 用于 trait 对象,必须将他们放入指针之后,比如 `&Trait``Box<Trait>``Rc<Trait>` 也可以。trait 之所以是动态大小类型的是因为只有这样才能使用它。
#### `Sized` trait #### `Sized` trait

@ -239,7 +239,7 @@ takes time, the `handle_connection` function could potentially finish and
unless we call `flush`. This is how streams work in many languages and is a unless we call `flush`. This is how streams work in many languages and is a
small detail I don't think is worth going into in depth. /Carol --> small detail I don't think is worth going into in depth. /Carol -->
有了这些修改,运行我们的代码并进行请求!我们不再向终端打印任何数据,所以不会再看到除了 Cargo 以外的任何输出。不过当在浏览器中加载 `127.0.0.1:8080` 时,会得到一个空页面而不是错误。太棒了!我们刚刚手写了一个 HTTP 请求与响应。 有了这些修改,运行我们的代码并进行请求!我们不再向终端打印任何数据,所以不会再看到除了 Cargo 以外的任何输出。不过当在浏览器中加载 `127.0.0.1:7878` 时,会得到一个空页面而不是错误。太棒了!我们刚刚手写了一个 HTTP 请求与响应。
### 返回真正的 HTML ### 返回真正的 HTML
@ -298,7 +298,7 @@ fn handle_connection(mut stream: TcpStream) {
使用 `cargo run` 运行程序,在浏览器加载 `127.0.0.1:7878`,你应该会看到渲染出来的 HTML 文件! 使用 `cargo run` 运行程序,在浏览器加载 `127.0.0.1:7878`,你应该会看到渲染出来的 HTML 文件!
目前忽略了 `buffer` 中的请求数据并无条件的发送了 HTML 文件的内容。这意味着如果尝试在浏览器中请求 `127.0.0.1:8080/something-else` 也会得到同样的 HTML 响应。如此其作用是非常有限的,也不是大部分 server 所做的让我们检查请求并只对格式良好well-formed的请求 `/` 发送 HTML 文件。 目前忽略了 `buffer` 中的请求数据并无条件的发送了 HTML 文件的内容。这意味着如果尝试在浏览器中请求 `127.0.0.1:7878/something-else` 也会得到同样的 HTML 响应。如此其作用是非常有限的,也不是大部分 server 所做的让我们检查请求并只对格式良好well-formed的请求 `/` 发送 HTML 文件。
### 验证请求并有选择的进行响应 ### 验证请求并有选择的进行响应

Loading…
Cancel
Save