Refining Ch04.

This commit is contained in:
rust-lang.xfoss.com 2023-12-13 09:17:52 +08:00
parent 73d1310b76
commit c3495c3ed9
2 changed files with 33 additions and 26 deletions

View File

@ -1,6 +1,6 @@
fn main() { fn main() {
let s1 = String::from("hello"); let s1 = String::from("hello");
let s2 = s1; let s2 = s1.clone();
println! ("{}, world!", s1); println! ("s1 = {s1}, s2 = {s2}");
} }

View File

@ -158,7 +158,7 @@ Rust 采取了不同路径:一旦拥有内存的变量超出作用域,该内
这种模式对 Rust 代码的编写方式,影响深远。现在看来可能很简单,但在当我们打算让多个变量,使用我们在堆上分配的数据,这种更为复杂的情况下,代码的行为就会出乎意料。现在我们来探讨一下,其中的一些情况。 这种模式对 Rust 代码的编写方式,影响深远。现在看来可能很简单,但在当我们打算让多个变量,使用我们在堆上分配的数据,这种更为复杂的情况下,代码的行为就会出乎意料。现在我们来探讨一下,其中的一些情况。
## 变量与数据相互作用:迁移 ### 变量与数据相互作用:迁移
**Variables and Data Interacting with Move** **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` 被失效后内存中的表示* *图 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** **Variables and Data Interacting with Clone**
在我们 *确实* 想要深度拷贝该 `String` 的堆数据,而不仅仅是其栈数据时,我们可以使用一个名为 `clone` 的常用方法。我们将在第 5 章讨论方法语法,但由于方法是许多编程语言的共同特征,你可能已经见过了。
*确实* 打算对 `String` 的内存堆数据,而非只是栈数据进行深度拷贝时,就可以使用一个常用的、名为 `clone` 的方法。在第 5 章将讨论到方法语法,而由于在众多编程语言中,方法都是共同特性,那么此前大概率是见到过方法的。 下面是这个 `clone` 方法的一个实际应用示例:
下面是一个运作中的 `clone` 方法示例:
```rust ```rust
fn main() { let s1 = String::from("hello");
let s1 = String::from("hello"); // 这里 s 的类型为String
let s2 = s1.clone(); let s2 = s1.clone();
println! ("s1 = {}, s2 = {}", s1, s2); println! ("s1 = {s1}, s2 = {s2}");
}
``` ```
这段代码工作起来毫无问题,并显式地产生出图 4-3 中给出的行为,其间内存堆数据确实得以拷贝。
当咱们看到对 `clone` 的调用时,咱们就知道一些任意的代码正在被执行,而这些代码可能开销很大。这是表明正在发生一些不同寻常事情的明显标志 这工作得很好,并会显式地产生如图 4-3 所示的行为,其中堆数据 *确实* 得以拷贝
## 唯栈数据:拷贝 当咱们看到某个对 `clone` 的调用时,咱们就知道一些任意代码正在被执行,而这些代码可能很昂贵。这是个直观的指示器,表明正在发生一些不同寻常的事情。
### 唯栈数据:拷贝
**Stack-Only Data: Copy** **Stack-Only Data: Copy**
还有一个咱们尚未谈到过的问题。使用整数的这段代码 -- 其中一部分已在清单 4-2 中给出 -- 是有效的:
尚有另一个至今还未讲到的小问题。正使用着整数的这段代码 -- 其中一部分在下面的清单 4-2 中给出了 -- 会工作并是有效代码:
```rust ```rust
let x = 5; let x = 5;
@ -331,25 +331,32 @@ let y = x;
println! ("x = {}, y = {}", x, y); 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` 那么,哪些类型实现了 `Copy` 特质呢?咱们可以查看给定类型的文档来确定,但一般来说,任何一组简单的标量值,都可以实现 `Copy`,而任何要求内存分配,或属于某种形式资源的类型,都不能实现 `Copy`。下面是一些实现了 `Copy` 的类型:
- 布尔值类型,`bool`,即值 `true``false`
- 全部浮点数类型,比如 `f64`; - 所有整数类型,如 `u32`
- 字符类型,`char`;
- 只包含实现 `Copy` 特质类型的元组类型。比如 `(i32, i32)` 这个元组类型,就实现了 `Copy` 特质,而 `(i32, String)` 则没有。 - 布尔类型,`bool`,值为 `true``false`
- 所有浮点类型,如 `f64`
- 字符类型 `char`
- 只包含同时实现了 `Copy` 类型的元组。例如,`(i32, i32)` 实现了 `Copy`,但 `(i32, String)` 则没有。
## 所有权与函数 ## 所有权与函数
**Ownership and Functions**
将值传递给函数的语法,与将值赋值给变量的语法,是类似的。将变量传递给函数,就会进行迁移或拷贝,这与赋值所做的别无二致。下面的清单 4-3 有着一个带有一些注解的示例,对其中的变量进入和超出作用域,进行了展示。 将值传递给函数的语法,与将值赋值给变量的语法,是类似的。将变量传递给函数,就会进行迁移或拷贝,这与赋值所做的别无二致。下面的清单 4-3 有着一个带有一些注解的示例,对其中的变量进入和超出作用域,进行了展示。