初步了解Rust所有权规则

所有权

所有权规则

首先,让我们来看看所有权规则。在我们处理说明它们的示例时,请记住这些规则:

  • Rust 中的每个值都有一个变量,称为其owner
  • 一次只能有一个所有者。
  • 当所有者超出范围时,该值将被删除。

移动

1
2
3
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1); // 错误!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");
// 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 是基本类型, 无需释放

克隆

如果要防止这种情况的出现,我们可以使用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");
// s1 是可变的

let s2 = &mut s1;
// s2 是可变的引用

s2.push_str("oob");
println!("{}", s2);

let s3 = &mut s1;//错误,禁止多重引用!
}