初步了解Rust所有权规则
所有权
所有权规则
首先,让我们来看看所有权规则。在我们处理说明它们的示例时,请记住这些规则:
- Rust 中的每个值都有一个变量,称为其owner。
- 一次只能有一个所有者。
- 当所有者超出范围时,该值将被删除。
移动
1 2 3
| let s1 = String::from("hello"); let s2 = s1; println!("{}, world!", s1);
|
理解上面这段代码,s1存在栈中,hello存在堆中,s1指向hello,当s1赋值给s2后,s1将会失效,也就是说Rust 自动调用释放资源函数并清理了s1变量的堆内存,现在s2将指向hello。
函数所有权
当调用了一个函数并传递变量参数后,这个参数将在原本进行传递的函数中失效,和移动的效果一致。如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| fn main() { let s = String::from("hello");
takes_ownership(s);
let x = 5;
makes_copy(x);
}
fn takes_ownership(some_string: String) { println!("{}", some_string); }
fn makes_copy(some_integer: i32) { println!("{}", some_integer); }
|
克隆
如果要防止这种情况的出现,我们可以使用clone方法来克隆s1的值。
1 2 3 4 5
| fn main() { let s1 = String::from("hello"); let s2 = s1.clone(); println!("s1 = {}, s2 = {}", s1, s2); }
|
这里是真的将堆中的 “hello” 复制了一份,所以 s1 和 s2 都分别绑定了一个值,释放的时候也会被当作两个资源。
当然,克隆仅在需要复制的情况下使用,毕竟复制数据会花费更多的时间。
引用与租借
引用
另一种方法就是引用,进行间接访问。
1 2 3 4 5
| fn main() { let s1 = String::from("hello"); let s2 = &s1; println!("s1 is {}, s2 is {}", s1, s2); }
|
这里s1指向堆中的hello,s2引用了s1,这样就可以看成s2指向s1,s1指向hello。这里没有复制s1的值,那么自然不属于移动,因此变量本身并不会失效。
1 2 3 4 5 6 7
| fn main() { let s1 = String::from("run"); let s2 = &s1; println!("{}", s2); s2.push_str("oob"); println!("{}", s2); }
|
这里s1并没有定义mut,因此引用不具有所有权,也就是说并不能从s2修改数据。
被切片引用的字符串禁止更改其值(这里不管是不是租借都不可以!)
1 2 3 4 5 6
| fn main() { let mut s = String::from("runoob"); let slice = &s[0..3]; s.push_str("yes!"); println!("slice = {}", slice); }
|
租借
但如果s1指定了mut,那么这个参数就是一个可变参数,因此s2就可以修改数据,这里称之为租借。
但是禁止多重引用。
1 2 3 4 5 6 7 8 9 10 11 12
| fn main() { let mut s1 = String::from("run");
let s2 = &mut s1;
s2.push_str("oob"); println!("{}", s2); let s3 = &mut s1; }
|