diff --git a/source/_posts/Rust.md b/source/_posts/Rust.md index 355887c..8f4368d 100644 --- a/source/_posts/Rust.md +++ b/source/_posts/Rust.md @@ -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 是基本类型, 无需释放 +``` + +如果将变量当作参数传入函数,那么它和移动的效果是一样的。