Rust 所有权
Rust 所有权
所有权是一组控制 Rust 程序如何管理内存的规则,rust 使用内存通过所有权系统进行管理,该系统具有一组编译器检查的规则。如果违反任何规则,程序将无法编译。
规则
- Rust 中的每个值都有一个称为其 owner 的变量
- 一个值在同一时间只能有一个 owner
- 当 owner 变量超出作用域时,值会被销毁
数据存放
- 变量信息存放在堆(stack)中
- 值信息存放在栈(heap)中
引用(reference)
引用就像一个指针,因为它是一个地址,可以按照它来访问存储在该地址的数据,该地址由其他变量拥有。与指针不同,引用保证指向特定类型的有效值。
变量对引用没有所有权。
通过 & 来获取变量的引用,又称为解引用,使用 & 关键字修饰的类型称为引用类型,使用 & 关键字修饰的变量称为引用变量。
1
2
3
4
5
fn say(str: &String){ // 引用类型
println!("{}", str);
}
let s = String::from("Hello");
say(&s); // 传入引用变量
引用的规则:
- 在任何给定时间,都可以拥有一个可变引用或任意数量的不可变引用
- 引用必须始终有效。
转移(transfer)
声明一个结构变量,会在堆上创建一个变量信息,在栈上创建值信息,并通过引用关联变量和值。
1
let s1 = String::from("Hello");
通过赋值表达式,s2 会在堆中拷贝一份 s1 的变量信息,并将引用也交给 s2,交出引用后,s1 会被标记为非法,指针转移过程称为所有权转移。
1
2
let s2 = s1;
println!("{}, world!", s1); // Error: value borrowed here after move
作为函数参数传入会发生所有权转移。
1
2
3
4
5
6
7
8
fn say(word: String){
println!("{}", word);
}
fn main(){
let s = String::from("Tom");
say(s);
println!("{}", s); // borrow of moved value: `x`
}
作为函数的返回值返回后,会发生所有权转移。
1
2
3
4
5
fn gives_ownership() -> String {
let some_string = String::from("move");
some_string
}
let s2 = gives_ownership(); // some_string的所有权转移给了s2
所有权的转移遵循一个标准模式:一个变量赋值给另一个变量。
这种模式同样包括元组赋值。
1
2
3
let x = String::from("hello");
let t = (12, x);
println!("{}", x); // borrow of moved value: `x`
克隆(clone)
通过 clone 接口实现值的克隆,避免所有权转移。
1
2
3
4
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
堆存放的信息默认实现 copy 接口。其中包括以下数据类型:
- 整型
- 布尔型
- 浮点型
- 字符型
- 元组
借用(borrow)
传递应用或引用赋值的动作称为借用。借用分为不可变借用和可变借用,其中不可变借用可以被无限次借用。
1
2
3
4
5
6
7
8
9
10
fn main() {
let s = String::from("hello");
let s1 = &s; // 借用
change(&s); // 借用
}
fn change(some_string: &String) {
some_string.push_str(", world");
}
当引用被借用后,就不可以进行可变借用。
1
2
3
4
let mut s = String::from("aaa");
let s1 = &mut s;
let s2 = &mut s; // Error: cannot borrow `s` as mutable more than once at a time
println!("{}, {}", s1, s2);
引用借用会造成数据竞争,例如下列情况:
- 两个或多个指针同时访问相同的数据
- 至少有一个指针用于写入数据
- 没有用于同步数据访问的机制
当不可变借用被消费后,可以继续进行可变借用。
1
2
3
4
5
6
7
8
9
let mut s = String::from("hello");
let r1 = &s; // no problem
let r2 = &s; // no problem
println!("{} and {}", r1, r2);
// variables r1 and r2 will not be used after this point
let r3 = &mut s; // no problem
println!("{}", r3);
This post is licensed under
CC BY 4.0
by the author.