OBRM与内存管理
[[pointer-and-reference]]
原则:
- Each value in Rust has a variable that's called its owner.
- There can be only one owner at a time.
- When the owner goes out of the scope, the value will be dropped.
在变量赋值的情况
所有权会发生转移。
let s1 = String::from("example"); // 这个字符串存储在堆上
let s2 = s1; // s1的内容所有权已经转移给s2
println!("s1 is {s1}"); // 会报错
如果需要让s1能打印出来,可以让s2克隆s1。
let s1 = String::from("example");
let s2 = s1.clone(); // 将s1的内容克隆一份给s2
println!("s1 is {s1}"); // 不会报错
存储在栈上的变量会默认clone(例如数字、布尔值等),例如:
let x = 10;
let y = x; // 默认克隆
println!("x is {x}"); // 不会报错
在函数进出传递中的情况
将变量传递到函数当中与赋值有相同的效果,也就是把所有权转移给形参。形参在函数执行的末尾会被删除。
后面也可以用借用来解决这个问题。
print_string(s1.clone()); // 可以这样避免s1的所有权被转移
let s2 = generate_string(); // 函数返回值的所有权被转移到s2
存储在栈内存上的变量情况与上一个情况类似,会clone。
借用
为什么要借用:
- 更经济,不需要克隆。
- 调用不需要转移所有权的函数。
为了避免数据竞争(Data Races)和悬空引用(Dangling Reference),借用遵循两条规则:
- 只能有一个可变的引用,或无数个不可变的引用。
- 引用必须有效。
let r1 = &s1;
print_string(r1);
println!("s1 is {}", s1); // s1仍然有效
除了克隆和引用之外,还可以用shadowing来解决这个问题,但是看起来有点奇怪,而且效率不高。
let s1 = add_to_string(s1);
所以当调用需要修改内容的函数时,可以用可变引用。可变变量才能创建可变引用。
let r2 = &mut s1; // 可变引用
add_to_string(r2);
如果此时触犯第一条原则,我们需要调整引用使用的顺序。
let r1 = &s1; // 一个不可变引用
let r2 = &mut s1; // 无法创建可变引用
print_string(r1);
add_to_string(r2);
let r1 = &s1; // 一个不可变引用
print_string(r1);
let r2 = &mut s1; // 此时可以创建可变引用,因为不可变引用被删除了
add_to_string(r2);
解引用运算符是*,Rust有自动解引用的机制。