This commit is contained in:
文永达 2023-09-19 23:59:39 +08:00
parent b8fb4318ce
commit b25eeaaa54

View File

@ -620,3 +620,82 @@ let s2 = s1;
第二步的情况略有不同(这不是完全真的,仅用来对比参考):
![img](https://www.runoob.com/wp-content/uploads/2020/04/rust-ownership1.png)
如图所示:两个 String 对象在栈中,每个 String 对象都有一个指针指向堆中的 "hello" 字符串。在给 s2 赋值时,只有栈中的数据被复制了,堆中的字符串依然还是原来的字符串。
前面我们说过当变量超出范围时Rust 自动调用释放资源函数并清理该变量的堆内存。但是 s1 和 s2 都被释放的话堆区中的 "hello" 被释放两次,这是不被系统允许的。为了确保安全,在给 s2 赋值时 s1 已经无效了。没错,在把 s1 的值赋给 s2 以后 s1 将不可以再被使用。下面这段程序是错的:
```rust
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1); // 错误s1 已经失效
```
所以实际情况是:
![img](https://www.runoob.com/wp-content/uploads/2020/04/rust-ownership2.png)
s1 名存实亡。
### 克隆
Rust会尽可能地降低程序的运行成本所以默认情况下长度较大的数据存放在堆中且采用移动的方式进行数据交互。但如果需要将数据单纯的复制一份以供他用可以使用数据的第二种交互方式——克隆。
```rust
fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();
    println!("s1 = {}, s2 = {}", s1, s2);
}
```
运行结果:
```shell
s1 = hello, s2 = hello
```
这里是真的将堆中的 "hello" 复制了一份,所以 s1 和 s2 都分别绑定了一个值,释放的时候也会被当作两个资源。
当然,克隆仅在需要复制的情况下使用,毕竟复制数据会花费更多的时间。
## 涉及函数的所有权限制
对于变量来说这是最复杂的情况了。
如果将一个变量当作函数的参数传给其他函数,怎样安全的处理所有权呢?
下面这段程序描述了这种情况下所有权机制的运行原理:
```rust
fn main() {
    let s = String::from("hello");
    // s 被声明有效
    takes_ownership(s);
    // s 的值被当作参数传入函数
    // 所以可以当作 s 已经被移动,从这里开始已经无效
    let x = 5;
    // x 被声明有效
    makes_copy(x);
    // x 的值被当作参数传入函数
    // 但 x 是基本类型,依然有效
    // 在这里依然可以使用 x 却不能使用 s
} // 函数结束, x 无效, 然后是 s. 但 s 已被移动, 所以不用被释放
fn takes_ownership(some_string: String) {
    // 一个 String 参数 some_string 传入,有效
    println!("{}", some_string);
} // 函数结束, 参数 some_string 在这里释放
fn makes_copy(some_integer: i32) {
    // 一个 i32 参数 some_integer 传入,有效
    println!("{}", some_integer);
} // 函数结束, 参数 some_integer 是基本类型, 无需释放
```
如果将变量当作参数传入函数,那么它和移动的效果是一样的。