Update chapter_3_7.md

This commit is contained in:
令狐一冲
2023-05-17 14:55:41 +08:00
committed by GitHub
parent dadd62db72
commit 9aeaae05cc

View File

@@ -171,5 +171,97 @@ fn main() {
```
s是String类型字符串“Hello world”是存储在堆内存上的其内存布局如下
![注释](../../assets/6.png)
当执行let s1 = s后内存布局如下
![注释](../../assets/7.png)
当let s1 = s执行后就发生了所有权的转移String类型值的所有权从s转移到了s1。此时Rust认为原来的s不再有效。因此上面代码第4行打开编译将会出错。
#### 5. 浅拷贝与深拷贝
- 浅拷贝
只拷贝栈上的内容,就叫做浅拷贝。
对于上面的String类型执行let s1 = s后只把s的ptr、len、cap中的值拷贝给s1的ptr、len、cap的值这种就叫做浅拷贝。浅拷贝发生后s的ptr和s1的ptr都指向同样的堆内存。内存布局如下
![注释](../../assets/8.png)
- 深拷贝
除了拷贝栈上的内容外,还拷贝堆内存中的内容,就叫做深拷贝。
对于上面的String类型执行let s1 = s后除了把s的len、cap中的值拷贝给s1的len、cap外还在堆上重新分配一块内存将s的ptr指向的堆内存的内容拷贝到这块内存然后s1的ptr指向这块内存这种拷贝就叫做深拷贝。深拷贝发生后s的ptr和s1的ptr指向不同的堆内存但是堆内存中存储的内容一样。深拷贝发生后的内存布局如下
![注释](../../assets/9.png)
显然,**Rust中变量赋值Rust中叫所有权转移使用的是浅拷贝。**
#### 6. Clone
当需要拷贝堆上的数据时可以使用clone方法完成深拷贝的操作如下
```Rust
fn main() {
let s = "Hello world!".to_string();
let s1 = s.clone(); // 这将发生深拷贝
println!("s: {:?}", s);
println!("s1: {:?}", s1);
}
```
不过不是所有的类型都能使用clone方法进行深拷贝只有实现了Clone trait的类型才能调用该方法。
#### 7. Copy
按照Rust所有权规则第二条在任意时刻值有且仅有一个所有者。所以当let a = b发生时就将变量b拥有的值移到了a上此时a应该回到未初始状态但实际情况并不一定。不一定的原因是部分类型实现了Copy trait在值移动时会对值进行自动拷贝能让变量a仍拥有原来的值。
Rust中默认实现了Copy trait的类型有
- 所有整数类型比如u32
- 所有浮点数类型比如f64
- 布尔类型bool它的值是true和false
- 字符类型char
- 元组当且仅当其包含的类型也都是Copy的时候。比如(i32, i32)是Copy的但(i32, String)不是;
- 共享指针类型或共享引用类型。
#### 8. 所有权和函数
- 将值传给函数
在将值传递给函数时,和变量赋值一样会发生值的移动(或复制),如下:
```Rust
fn main() {
let s = String::from("hello");
takes_ownership(s);
// println!("s: {:?}", s);//打开编译会报错因为s的所有权在上一行已经转移到take_ownership函数中了
let x = 5;
makes_copy(x);
println!("x: {:?}", x);//不会报错因为上一行将x传到makes_copy函数时会自动拷贝x的值到函数中
}
fn takes_ownership(some_string: String) {
println!("{}", some_string);
}
fn makes_copy(some_integer: i32) {
println!("{}", some_integer);
}
```
- 返回值和作用域
函数的返回值也可以转移所有权,如下:
```Rust
fn main() {
let s1 = gives_ownership(); // gives_ownership 将返回值转移给 s1
let s2 = String::from("hello"); // s2 进入作用域
let s3 = takes_and_gives_back(s2); // s2 被移动到takes_and_gives_back 中,
// 它也将返回值移给 s3
} //这里s3移出作用域并被丢弃。s2也移出作用域但已被移走所以什么也不会发生。s1离开作用域并被丢弃
fn gives_ownership() -> String {// gives_ownership 会将返回值移动给调用它的函数
let some_string = String::from("yours"); // some_string 进入作用域。
some_string // 返回 some_string 并移出给调用的函数
}
// takes_and_gives_back 将传入字符串并返回该值
fn takes_and_gives_back(a_string: String) -> String { // a_string 进入作用域
a_string // 返回 a_string 并移出给调用的函数
}
```
关于所有权的总结:将值赋给另一个变量时移动它。当持有堆中数据值的变量离开作用域时,其值将通过 drop函数后续讲解 被清理掉,除非数据被移动为另一个变量所有。