mirror of
https://github.com/gnu4cn/rust-lang-zh_CN.git
synced 2024-12-26 21:00:24 +08:00
Refining Ch04.
This commit is contained in:
parent
73d1310b76
commit
c3495c3ed9
@ -1,6 +1,6 @@
|
||||
fn main() {
|
||||
let s1 = String::from("hello");
|
||||
let s2 = s1;
|
||||
let s2 = s1.clone();
|
||||
|
||||
println! ("{}, world!", s1);
|
||||
println! ("s1 = {s1}, s2 = {s2}");
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ Rust 采取了不同路径:一旦拥有内存的变量超出作用域,该内
|
||||
这种模式对 Rust 代码的编写方式,影响深远。现在看来可能很简单,但在当我们打算让多个变量,使用我们在堆上分配的数据,这种更为复杂的情况下,代码的行为就会出乎意料。现在我们来探讨一下,其中的一些情况。
|
||||
|
||||
|
||||
## 变量与数据相互作用:迁移
|
||||
### 变量与数据相互作用:迁移
|
||||
|
||||
**Variables and Data Interacting with Move**
|
||||
|
||||
@ -287,42 +287,42 @@ error: could not compile `ownership_demo` (bin "ownership_demo") due to previous
|
||||
*图 4-4:变量 `s1` 被失效后内存中的表示*
|
||||
|
||||
|
||||
这就解决了问题!在只有 `s2` 有效之下,当变量 `s2` 超出作用域后,那么就只有他会释放内存,于是就解决了双重内存释放问题。
|
||||
这就解决了我们的问题!由于只有 `s2` 有效,当他超出作用域时,只有他将释放内存,我们就大功告成了。
|
||||
|
||||
此外,这种做法背后,还隐含着一种语言设计上的取舍:Rust 绝不会自动创建数据的 “深” 拷贝。由此,任何 *自动* 拷贝,都可认为在运行时性能开销上的影响很小(Therefore, any *automatic* copying can be assumed to be inexpensive in terms of runtime performance)。
|
||||
此外,这还暗含一种设计取舍: Rust 绝不会自动创建咱们数据的 “深” 拷贝。因此,可以假定任何 *自动* 拷贝,在运行时性能方面,都是低成本的。
|
||||
|
||||
|
||||
## 变量与数据交互方式之二:克隆
|
||||
### 变量与数据相互作用:克隆
|
||||
|
||||
**Variables and Data Interacting with Clone**
|
||||
|
||||
|
||||
在我们 *确实* 想要深度拷贝该 `String` 的堆数据,而不仅仅是其栈数据时,我们可以使用一个名为 `clone` 的常用方法。我们将在第 5 章讨论方法语法,但由于方法是许多编程语言的共同特征,你可能已经见过了。
|
||||
|
||||
在 *确实* 打算对 `String` 的内存堆数据,而非只是栈数据进行深度拷贝时,就可以使用一个常用的、名为 `clone` 的方法。在第 5 章将讨论到方法语法,而由于在众多编程语言中,方法都是共同特性,那么此前大概率是见到过方法的。
|
||||
下面是这个 `clone` 方法的一个实际应用示例:
|
||||
|
||||
下面是一个运作中的 `clone` 方法示例:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let s1 = String::from("hello"); // 这里 s 的类型为:String
|
||||
let s1 = String::from("hello");
|
||||
let s2 = s1.clone();
|
||||
|
||||
println! ("s1 = {}, s2 = {}", s1, s2);
|
||||
}
|
||||
println! ("s1 = {s1}, s2 = {s2}");
|
||||
```
|
||||
|
||||
这段代码工作起来毫无问题,并显式地产生出图 4-3 中给出的行为,其间内存堆数据确实得以拷贝。
|
||||
|
||||
当咱们看到对 `clone` 的调用时,咱们就知道一些任意的代码正在被执行,而这些代码可能开销很大。这是表明正在发生一些不同寻常事情的明显标志。
|
||||
这工作得很好,并会显式地产生如图 4-3 所示的行为,其中堆数据 *确实* 得以拷贝。
|
||||
|
||||
|
||||
## 唯栈数据:拷贝
|
||||
当咱们看到某个对 `clone` 的调用时,咱们就知道一些任意代码正在被执行,而这些代码可能很昂贵。这是个直观的指示器,表明正在发生一些不同寻常的事情。
|
||||
|
||||
|
||||
### 唯栈数据:拷贝
|
||||
|
||||
**Stack-Only Data: Copy**
|
||||
|
||||
|
||||
还有一个咱们尚未谈到过的问题。使用整数的这段代码 -- 其中一部分已在清单 4-2 中给出 -- 是有效的:
|
||||
|
||||
尚有另一个至今还未讲到的小问题。正使用着整数的这段代码 -- 其中一部分在下面的清单 4-2 中给出了 -- 会工作并是有效代码:
|
||||
|
||||
```rust
|
||||
let x = 5;
|
||||
@ -331,25 +331,32 @@ let y = x;
|
||||
println! ("x = {}, y = {}", x, y);
|
||||
```
|
||||
|
||||
然而这段代码,似乎与前面刚刚所掌握的相抵触:这里没有对 `clone` 的调用,但变量 `x` 依然有效,而并未迁移到变量 `y` 中去。
|
||||
|
||||
原因就在于,诸如整数这样的,在编译时大小已知的类型,都是被整个存储在栈上,那么构造他们具体值的拷贝是迅速的。那就意味着,在构造出变量 `y` 之后,就没有理由要去阻止变量 `x` 一直有效了。换句话说,此时的深拷贝与浅拷贝之间,是没有区别的,因此对 `clone` 进行调用,不会完成与通常的浅拷贝有任何区别的事情,进而就能忽略这个 `clone` 方法。
|
||||
但这段代码似乎与我们刚刚学到的内容相矛盾:我们没有调用 `clone`,但 `x `仍然有效,而没有被迁移到 `y` 中。
|
||||
|
||||
Rust 有着叫做 `Copy` 特质(the `Copy` trait, 在第 10 章将对特质,traits,进行更多的讲解)的,可放在像是整数这样的、存储于栈上的那些类型之上的一个特殊注解,a special annotation。在某个类型实现了 `Copy` 特质时,使用此类型的那些变量,就不会迁移,相反会轻而易举地被复制,从而在赋值给另一变量后,令到他们依然有效。
|
||||
原因是像整数这种在编译时就已知大小的类型,会被整个地存储在栈上,所以具体值的拷贝很快就构造出来了。换句话说,这里的深拷贝和浅拷贝,没有区别,所以调用 `clone` 与通常的浅拷贝,就不会有什么不同,我们可以不使用他。
|
||||
|
||||
在某个类型或类型的任何部分带有 `Copy` 特质时,Rust 就不会再允许以 `Drop` 特质对其加以注解了。若某个类型需要在其值超出作用域后,还要进行某些特殊处理,而又将 `Copy` 注解添加到了那个类型,那么就会收到编译时错误(if the type needs something special to happen when the value goes out of scope and we add the `Copy` annotation to that type, we'll get a compile-time error)。要了解如何将 `Copy` 注解,添加到自己编写的类型而实现这个 `Copy` 特质,请参阅附录 C 中 [可派生特质(derivable traits)](Ch21_Appendix.md#附录-c派生特质)。
|
||||
Rust 有个名为 `Copy` 特质的特殊注解,我们可以把他放在像整数这种,存储在栈上的类型上(我们将在 [第 10 章](../generic_types_traits_and_lifetimes/traits.md) 详细讨论特质)。在某个类型实现了 `Copy` 特质时,用到他的变量就不会迁移,而是被简单拷贝,从而令到他们在赋值给另一个变量后,仍然有效。
|
||||
|
||||
那么到底哪些类型要实现 `Copy` 特质呢?可查阅给定类型的文档,来确定相应类型是否有实现 `Copy` 特质,不过作为一般规则,任何组别的简单标量值,any group of simple scalar values,都可实现 `Copy` 特质,以及不要求分配内存堆分配,或者其他形式资源的类型,也都可以实现 `Copy` 特质(any group of simple scalar values can implement `Copy`, and nothing that requires allocation or is some form of resource can implement `Copy`)。下面就是一些实现 `Copy` 特质的类型:
|
||||
如果某个类型或其任何部分,已经实现了 `Drop` 这个特质,Rust 就不会让我们以 `Copy` 注解该类型了。如果该类型需要在其值超出作用域时,发生某些特殊事情,且我们又为该类型添加了 `Copy` 注解,我们将得到一个编译时报错。要了解如何把 `Copy` 注解添加到咱们的类型,以实现这个特质,请参阅附录 C 中的 [派生特质](../appendix/derivable_traits.md)。
|
||||
|
||||
- 全部的整型,比如 `u32`;
|
||||
- 布尔值类型,`bool`,即值 `true` 与 `false`;
|
||||
- 全部浮点数类型,比如 `f64`;
|
||||
- 字符类型,`char`;
|
||||
- 只包含实现 `Copy` 特质类型的元组类型。比如 `(i32, i32)` 这个元组类型,就实现了 `Copy` 特质,而 `(i32, String)` 则没有。
|
||||
那么,哪些类型实现了 `Copy` 特质呢?咱们可以查看给定类型的文档来确定,但一般来说,任何一组简单的标量值,都可以实现 `Copy`,而任何要求内存分配,或属于某种形式资源的类型,都不能实现 `Copy`。下面是一些实现了 `Copy` 的类型:
|
||||
|
||||
- 所有整数类型,如 `u32`;
|
||||
|
||||
- 布尔类型,`bool`,值为 `true` 及 `false`;
|
||||
|
||||
- 所有浮点类型,如 `f64`;
|
||||
|
||||
- 字符类型 `char`;
|
||||
|
||||
- 只包含同时实现了 `Copy` 类型的元组。例如,`(i32, i32)` 实现了 `Copy`,但 `(i32, String)` 则没有。
|
||||
|
||||
|
||||
## 所有权与函数
|
||||
|
||||
**Ownership and Functions**
|
||||
|
||||
|
||||
将值传递给函数的语法,与将值赋值给变量的语法,是类似的。将变量传递给函数,就会进行迁移或拷贝,这与赋值所做的别无二致。下面的清单 4-3 有着一个带有一些注解的示例,对其中的变量进入和超出作用域,进行了展示。
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user