mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 00:43:59 +08:00
commit
731f2d0953
@ -4,7 +4,7 @@
|
||||
> <br>
|
||||
> commit 4f2dc564851dc04b271a2260c834643dfd86c724
|
||||
|
||||
`cargo install`命令用于在本地安装和使用二进制 crate。它并不打算替换系统中的包;它意在作为一个方便 Rust 开发者安装他人在 crates.io 共享的工具的手段。只有有二进制目标文件的包能够安装,而且所有二进制文件都被安装到 Rust 安装根目录的 *bin* 文件夹中。如果你使用 *rustup.rs* 安装的 Rust 且没有自定义任何配置,这将是`$HOME/.cargo/bin`。将这个目录添加到`$PATH`环境变量中就能够运行通过`cargo install`安装的程序了。
|
||||
`cargo install`命令用于在本地安装和使用二进制 crate。它并不打算替换系统中的包;它意在作为一个方便 Rust 开发者们安装其他人已经在 crates.io 上共享的工具的手段。只有有二进制目标文件的包能够安装,而且所有二进制文件都被安装到 Rust 安装根目录的 *bin* 文件夹中。如果你使用 *rustup.rs* 安装的 Rust 且没有自定义任何配置,这将是`$HOME/.cargo/bin`。将这个目录添加到`$PATH`环境变量中就能够运行通过`cargo install`安装的程序了。
|
||||
|
||||
例如,第十二章提到的叫做`ripgrep`的用于搜索文件的`grep`的 Rust 实现。如果想要安装`ripgrep`,可以运行如下:
|
||||
|
||||
|
@ -34,7 +34,7 @@ enum List {
|
||||
<span class="caption">Listing 15-2: The first attempt of defining an enum to
|
||||
represent a cons list data structure of `i32` values</span>
|
||||
|
||||
我们实现了一个只存放`i32`值的 cons list。也可以选择实用第十章介绍的泛型来实现一个类型无关的 cons list。
|
||||
我们实现了一个只存放`i32`值的 cons list。也可以选择使用第十章介绍的泛型来实现一个类型无关的 cons list。
|
||||
|
||||
> #### cons list 的更多内容
|
||||
>
|
||||
@ -128,7 +128,7 @@ fn main() {
|
||||
<span class="caption">Listing 15-5: Definition of `List` that uses `Box<T>` in
|
||||
order to have a known size</span>
|
||||
|
||||
这样编译器就能够计算出储存一个`List`值需要的大小了。Rust 将会检查`List`,同样的从`Cons`成员开始检查。`Cons`成员需要`i32`的大小加上一个`usize`的大小,因为 box 总是`usize`大小的,不管它指向的是什么。接着 Rust 检查`Nil`成员,它并储存一个值,所以`Nil`并不需要任何空间。我们通过 box 打破了这无限递归的连锁。图 15-6 展示了现在`Cons`成员看起来像什么:
|
||||
这样编译器就能够计算出储存一个`List`值需要的大小了。Rust 将会检查`List`,同样的从`Cons`成员开始检查。`Cons`成员需要`i32`的大小加上一个`usize`的大小,因为 box 总是`usize`大小的,不管它指向的是什么。接着 Rust 检查`Nil`成员,它并不储存一个值,所以`Nil`并不需要任何空间。我们通过 box 打破了这无限递归的连锁。图 15-6 展示了现在`Cons`成员看起来像什么:
|
||||
|
||||
<img alt="A finite Cons list" src="img/trpl15-02.svg" class="center" />
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
> <br>
|
||||
> commit ecc3adfe0cfa0a4a15a178dc002702fd0ea74b3f
|
||||
|
||||
第一个智能指针相关的重要 trait 是`Deref`,它允许我们重载`*`,解引用运算符(不同于乘法运算符和全局引用运算符)。重载智能指针的`*`能使访问其后的数据更为方便,在这个部分的稍后介绍解引用强制多态时我们会讨论方便的意义。
|
||||
第一个智能指针相关的重要 trait 是`Deref`,它允许我们重载`*`,解引用运算符(不同于乘法运算符和全局引用运算符)。重载智能指针的`*`能使访问其持有的数据更为方便,在本章结束前谈到解引用强制多态时我们会说明方便的意义。
|
||||
|
||||
第八章的哈希 map 的“根据旧值更新一个值”部分简要的提到了解引用运算符。当时有一个可变引用,而我们希望改变这个引用所指向的值。为此,首先我们必须解引用。这是另一个使用`i32`值引用的例子:
|
||||
|
||||
@ -19,7 +19,7 @@ let mut x = 5;
|
||||
assert_eq!(6, x);
|
||||
```
|
||||
|
||||
我们使用`*y`来访问可变引用`y`所指向的数据,而是可变引用本身。接着可以修改它的数据,在这里对其加一。
|
||||
我们使用`*y`来访问可变引用`y`所指向的数据,而不是可变引用本身。接着可以修改它的数据,在这里对其加一。
|
||||
|
||||
引用并不是智能指针,他们只是引用指向的一个值,所以这个解引用操作是很直接的。智能指针还会储存指针或数据的元数据。当解引用一个智能指针时,我们只想要数据,而不需要元数据。我们希望能在使用常规引用的地方也能使用智能指针。为此,可以通过实现`Deref` trait 来重载`*`运算符的行为。
|
||||
|
||||
@ -69,7 +69,7 @@ struct that holds mp3 file data and metadata</span>
|
||||
*(my_favorite_song.deref())
|
||||
```
|
||||
|
||||
这个就是`self.audio`中的结果值。`deref`返回一个引用并接下来必需解引用而不是直接返回值的原因是所有权:如果`deref`方法直接返回值而不是引用,其值将被移动出`self`。这里和大部分使用解引用运算符的地方并不想获取`my_favorite_song.audio`的所有权。
|
||||
这个就是`self.audio`中的结果值。`deref`返回一个引用并接下来必需解引用而不是直接返回值的原因是所有权:如果`deref`方法直接返回值而不是引用,其值将被移动出`self`。和大部分使用解引用运算符的地方相同,这里并不想获取`my_favorite_song.audio`的所有权。
|
||||
|
||||
注意将`*`替换为`deref`调用和`*`调用的过程在每次使用`*`的时候都会发生一次。`*`的替换并不会无限递归进行。最终的数据类型是`Vec<u8>`,它与列表 15-7 中`assert_eq!`的`vec![1, 2, 3]`相匹配。
|
||||
|
||||
@ -111,4 +111,4 @@ Rust 在发现类型和 trait 实现满足三种情况时会进行解引用强
|
||||
|
||||
头两个情况除了可变性之外是相同的:如果有一个`&T`,而`T`实现了返回`U`类型的`Deref`,可以直接得到`&U`。对于可变引用也是一样。最后一个有些微妙:如果有一个可变引用,它也可以强转为一个不可变引用。反之则是_不可能_的:不可变引用永远也不能强转为可变引用。
|
||||
|
||||
`Deref` trait 对于只能指针模式十分重要的原因在于智能指针可以被看作普通引用并用于期望使用引用的地方。例如,无需重新编写方法和函数来直接获取智能指针。
|
||||
`Deref` trait 对于智能指针模式十分重要的原因在于智能指针可以被看作普通引用并被用于期望使用普通引用的地方。例如,无需重新编写方法和函数来直接获取智能指针。
|
@ -6,11 +6,11 @@
|
||||
|
||||
对于智能指针模式来说另一个重要的 trait 是`Drop`。`Drop`运行我们在值要离开作用域时执行一些代码。智能指针在被丢弃时会执行一些重要的清理工作,比如释放内存或减少引用计数。更一般的来讲,数据类型可以管理多于内存的资源,比如文件或网络连接,而使用`Drop`在代码处理完他们之后释放这些资源。我们在智能指针上下文中讨论`Drop`是因为其功能几乎总是用于实现智能指针。
|
||||
|
||||
在其他一些语言中,必须每次总是必须记住在使用完智能指针实例后调用清理内存或资源的代码。如果忘记的话,运行代码的系统可能会因为负荷过重而崩溃。在 Rust 中,可以指定一些代码应该在值离开作用域时被执行,而编译器会自动插入这些代码。这意味着无需记住在所有处理完这些类型实例后调用清理代码,而仍然不会泄露资源!
|
||||
在其他一些语言中,我们不得不记住在每次使用完智能指针实例后调用清理内存或资源的代码。如果忘记的话,运行代码的系统可能会因为负荷过重而崩溃。在 Rust 中,可以指定一些代码应该在值离开作用域时被执行,而编译器会自动插入这些代码。这意味着无需记住在所有处理完这些类型实例后调用清理代码,而仍然不会泄露资源!
|
||||
|
||||
指定在值离开作用域时应该执行的代码的方式是实现`Drop` trait。`Drop` trait 要求我们实现一个叫做`drop`的方法,它获取一个`self`的可变引用。
|
||||
|
||||
列表 15-8 展示了并没有实际功能的结构体`CustomSmartPointer`,不过我们会在创建实例之后打印出`CustomSmartPointer created.`,而在实例离开作用域时打印出`Dropping CustomSmartPointer!`,这样就能看出哪些代码被执行了。不同于`println!`语句,我们在智能指针需要执行清理代码时使用`drop`:
|
||||
列表 15-8 展示了并没有实际功能的结构体`CustomSmartPointer`,不过我们会在创建实例之后打印出`CustomSmartPointer created.`,而在实例离开作用域时打印出`Dropping CustomSmartPointer!`,这样就能看出每一段代码是何时被执行的。实际的项目中,我们应该在`drop`中清理任何智能指针运行所需要的资源,而不是这个例子中的`println!`语句:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
@ -36,7 +36,7 @@ fn main() {
|
||||
implements the `Drop` trait, where we could put code that would clean up after
|
||||
the `CustomSmartPointer`.</span>
|
||||
|
||||
`Drop` trait 位于 prelude 中,所以无需导入它。`drop`方法的实现调用了`println!`;这里是你需要实际需要放入关闭套接字代码的地方。在`main`函数中,我们创建一个`CustomSmartPointer`的新实例并打印出`CustomSmartPointer created.`以便在运行时知道代码运行到此出。在`main`的结尾,`CustomSmartPointer`的实例会离开作用域。注意我们没有显式调用`drop`方法:
|
||||
`Drop` trait 位于 prelude 中,所以无需导入它。`drop`方法的实现调用了`println!`;这里是你需要放入实际关闭套接字代码的地方。在`main`函数中,我们创建一个`CustomSmartPointer`的新实例并打印出`CustomSmartPointer created.`以便在运行时知道代码运行到此处。在`main`的结尾,`CustomSmartPointer`的实例会离开作用域。注意我们没有显式调用`drop`方法:
|
||||
|
||||
当运行这个程序,我们会看到:
|
||||
|
||||
|
@ -62,7 +62,7 @@ error[E0382]: use of moved value: `a`
|
||||
|
||||
`Cons`成员拥有其储存的数据,所以当创建`b`列表时将`a`的所有权移动到了`b`。接着当再次尝使用`a`创建`c`时,这不被允许因为`a`的所有权已经被移动。
|
||||
|
||||
相反可以改变`Cons`的定义来存放一个引用,不过接着必须指定生命周期参数,而且也必须以使得列表的每一个元素都与列表本身存在的一样久那样构造列表的元素。否则借用检查器甚至都不会允许我们编译代码。
|
||||
相反可以改变`Cons`的定义来存放一个引用,不过接着必须指定生命周期参数,而且在构造列表时,也必须使列表中的每一个元素都至少与列表本身存在的一样久。否则借用检查器甚至都不会允许我们编译代码。
|
||||
|
||||
如列表 15-12 所示,可以将`List`的定义从`Box<T>`改为`Rc<T>`:
|
||||
|
||||
@ -91,7 +91,7 @@ fn main() {
|
||||
|
||||
### 克隆`Rc<T>`会增加引用计数
|
||||
|
||||
之前我们见过`clone`方法,当时使用它来创建某些数据的完整拷贝。但是对于`Rc<T>`来说,它并不创建一个完整的拷贝。`Rc<T>`存放了**引用计数**,也就是说,一个存在多少个克隆的数量。让我们像列表 15-13 那样在创建`c`时增加一个内部作用域,并在不同的位置打印出关联函数`Rc::strong_count`的结果。`Rc::strong_count`返回传递给它的`Rc`值的引用计数,而在本章的稍后部分介绍避免引用循环时讲到它为什么叫做`strong_count`。
|
||||
之前我们见过`clone`方法,当时使用它来创建某些数据的完整拷贝。但是对于`Rc<T>`来说,它并不创建一个完整的拷贝。`Rc<T>`存放了**引用计数**,也就是说,一个存在多少个克隆的计数器。让我们像列表 15-13 那样在创建`c`时增加一个内部作用域,并在不同的位置打印出关联函数`Rc::strong_count`的结果。`Rc::strong_count`返回传递给它的`Rc`值的引用计数,而在本章的稍后部分介绍避免引用循环时讲到它为什么叫做`strong_count`。
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
|
||||
对于引用和`Box<T>`,借用规则的不可变性作用于编译时。对于`RefCell<T>`,这些不可变性作用于**运行时**。对于引用,如果违反这些规则,会得到一个编译错误。而对于`RefCell<T>`,违反这些规则会`panic!`。
|
||||
|
||||
Rust 编译器执行的静态分析时天生保守的。代码的一些属性则不可能通过分析代码发现:其中最著名的就是停机问题(停机问题),这超出了本书的范畴,不过如果你感兴趣的话这是一个值得研究的有趣主题。
|
||||
Rust 编译器执行的静态分析天生是保守的。代码的一些属性则不可能通过分析代码发现:其中最著名的就是停机问题(停机问题),这超出了本书的范畴,不过如果你感兴趣的话这是一个值得研究的有趣主题。
|
||||
|
||||
因为一些分析是不可能的,Rust 编译器在其不确定的时候甚至都不尝试猜测,所以说它是保守的而且有时会拒绝事实上不会违反 Rust 保证的正确的程序。换句话说,如果 Rust 接受不正确的程序,那么人们也就不会相信 Rust 所做的保证了。如果 Rust 拒绝正确的程序,会给程序员带来不变,但不会带来灾难。`RefCell<T>`正是用于当你知道代码遵守借用规则,而编译器不能理解的时候。
|
||||
|
||||
@ -116,7 +116,7 @@ thread 'main' panicked at 'already borrowed: BorrowMutError',
|
||||
note: Run with `RUST_BACKTRACE=1` for a backtrace.
|
||||
```
|
||||
|
||||
这个运行时`BorrowMutError`类似于编译错误:它表明已经可变的借用过`s`一次了,所以不允许再次借用它。我们并没有绕过借用规则,只是选择让 Rust 在运行时而不是编译时执行他们。你可以选择在任何时候任何地方使用`RefCell<T>`,不过除了不得不编写很多`RefCell`之外,最终还是可能会发现其中的问题(可能是在生产环境而不是开发环境)。另外,在运行时检查借用规则有性能惩罚。
|
||||
这个运行时`BorrowMutError`类似于编译错误:它表明我们已经可变得借用过一次`s`了,所以不允许再次借用它。我们并没有绕过借用规则,只是选择让 Rust 在运行时而不是编译时执行他们。你可以选择在任何时候任何地方使用`RefCell<T>`,不过除了不得不编写很多`RefCell`之外,最终还是可能会发现其中的问题(可能是在生产环境而不是开发环境)。另外,在运行时检查借用规则有性能惩罚。
|
||||
|
||||
### 结合`Rc<T>`和`RefCell<T>`来拥有多个可变数据所有者
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
在使用`Rc<T>`和`RefCell<T>`时,有可能创建循环引用,这时各个项相互引用并形成环。这是不好的因为每一项的引用计数将永远也到不了 0,其值也永远也不会被丢弃。让我们看看这是如何发生的以及如何避免它。
|
||||
|
||||
在列表 15-16 中,我们将使用列表 15-5 中`List`定义的另一个变体。我们将回到储存`i32`值作为`Cons`成员的第一个元素。现在`Cons`成员的第二个元素是`RefCell<Rc<List>>`:这时就不能修改`i32`值了,但是能够修改`Cons`成员指向的哪个`List`。还需要增加一个`tail`方法来方便我们在拥有一个`Cons`成员时访问第二个项:
|
||||
在列表 15-16 中,我们将使用列表 15-5 中`List`定义的另一个变体。我们将回到储存`i32`值作为`Cons`成员的第一个元素。现在`Cons`成员的第二个元素是`RefCell<Rc<List>>`:这时就不能修改`i32`值了,但是能够修改`Cons`成员指向的那个`List`。还需要增加一个`tail`方法来方便我们在拥有一个`Cons`成员时访问第二个项:
|
||||
|
||||
<span class="filename">Filename: src/main.rs</span>
|
||||
|
||||
|
@ -9,13 +9,13 @@
|
||||
|
||||
最开始,我们认为内存安全和防止并发问题是需要通过两个不同的方法解决的两个相互独立的挑战。然而,随着时间的推移,我们发现所有权和类型系统是一系列解决内存安全**和**并发问题的强用力的工具!通过改进所有权和类型检查,很多并发错误在 Rust 中都是**编译时**错误,而不是运行时错误。我们给 Rust 的这一部分起了一个绰号**无畏并发**(*fearless concurrency*)。无畏并发意味着 Rust 不光允许你自信代码不会出现诡异的错误,也让你可以轻易重构这种代码而无需担心会引入新的 bug。
|
||||
|
||||
> 注意:对于 Rust 的口号**无畏并发**,这里用**并发**指代很多问题而不是更精确的区分**并发和(或)并行**,是处于简化问题的原因。如果这是一本专注于并发和/或并行的书,我们肯定会更精确的。对于本章,请自行脑补任何**并发**为**并发和(或)并行**。
|
||||
> 注意:对于 Rust 的口号**无畏并发**,这里用**并发**指代很多问题而不是更精确的区分**并发和(或)并行**,是出于简化问题的原因。如果这是一本专注于并发和/或并行的书,我们肯定会更精确的。对于本章,当我们谈到**并发**时,请自行替换为**并发和(或)并行**。
|
||||
|
||||
很多语言对于其所提供的处理并发并发问题的解决方法是非常固执己见的。这是一个非常合理的策略,尤其是对于更高级的语言来说,不过对于底层语言来说可没有奢侈的选择。底层语言被期望为能在任何给定的场景中启用提供最高性能的方法,同时他们对硬件有更少的抽象。因此,Rust 给了我们多种工具来以适合场景和要求的方式来为问题建模。
|
||||
很多语言所提供的处理并发问题的解决方法是非常有自身特色的。这是一个非常合理的策略,尤其是对于更高级的语言来说,不过对于底层语言来说可没有奢侈的选择。底层语言被期望为在任何给定的情况下,都可以使能提供最高性能的解决方案可行,同时他们对硬件有更少的抽象。因此,Rust 给了我们多种工具并以各种适合我们的情况和要求的方式来为问题建模。
|
||||
|
||||
如下是本章将要涉及到的内容:
|
||||
|
||||
* 如果创建线程来同时运行多段代码。
|
||||
* 如何创建线程来同时运行多段代码。
|
||||
* 并发**消息传递**(*Message passing*),其中通道(channel)被用来在线程间传递消息。
|
||||
* 并发**共享状态**(*Shared state*),其中多个线程可以访问同一片数据。
|
||||
* `Sync`和`Send` trait,他们允许 Rust 的并发保证能扩展到用户定义的和标准库中提供的类型中。
|
||||
* `Sync`和`Send` trait,他们允许 Rust 的并发保证能被扩展到用户定义的和标准库中提供的类型中。
|
Loading…
Reference in New Issue
Block a user