Rust 所有权与移动
谈及内存管理,我们希望编程语言能具备两个特点:
- (控制优先)希望内存可以在我们选定的时机进行释放, 这使我们能控制程序的内存消耗;
- (安全优先)在对象被释放后,我们绝不希望继续使用指向它的指针,这是未定义行为,会导致崩溃和安全漏洞。
所有权
可以理解为这个参数的拥有者是谁,每个变量都将只会有一个拥有者.
变量拥有自己的值,当控制流离开声明变量的块时,变量就会被丢弃,因此它的值也会一起被丢弃。
fn print_padovan() {
let mut padovan = vec![1,1,1]; // 在此分配
for i in 3..10 {
let next = padovan[i-3] + padovan[i-2];
padovan.push(next);
}
println!("P(1..10) = {:?}", padovan);
} // 在此丢弃
理解起来就是 变量离开了自己的作用域那么这个参数就会被释放掉
移动
在 Rust 中,对大多数类型来说,像为变量赋值、将其传给函数或从函数返回这样的操作都不会复制值,而是会移动值。会把值的所有权转移给目标并变回未初始化状态,改由目标变量来控制值的生命周期。Rust 程序会以每次只移动一个值的方式建立和拆除复杂的结构。
**例子 1 **
let s = vec!["udon".to_string(), "ramen".to_string(), "soba".to_string()];
let t = s;
let u = s;
**在以上代码写法在 Java 中不会出错,但是在 Rust 其就会出现异常,其在 s 赋值给 t 时,他会把 s 重新初始化.并把值给与 t,所有在 **let u = s
就会出现问题,因为 s
已经是一个空对象.
**例子 2 **
let x = vec![10, 20, 30];
if c {
f(x); // ……可以在这里移动x
} else {
g(x); // ……也可以在这里移动x
}
h(x); // 错误:只要任何一条路径用过它,x在这里就是未初始化状态
在以上代码写法在 Java 中不会出错,但是在 Rust 其就会出现异常,因为一但 x 变量被使用过那么,他的所有权就被移动了.假如我们想要得到 f 或 g 修改过的结果,那么就需要在 f 或 g 进行返回.
let mut x = vec![10, 20, 30];
while f() {
g(x); // 从x移动出去了
x = h(); // 赋予x一个新值
}
e(x);
但并非值的每种拥有者都能变成未初始化状态。
// 构建一个由字符串"101"、"102"……"105"组成的向量
let mut v = Vec::new();
for i in 101 .. 106 {
v.push(i.to_string());
}
// 从向量中随机抽取元素
let third = v[2]; // 错误:不能移动到 Vec 索引结构之外<sup><b>3</b></sup>
let fifth = v[4]; // 这里也一样
因为 Vec 不能将对象移出,使用以下方法就可以。
// 构建一个由字符串"101"、"102"……"105"组成的向量
let mut v = Vec::new();
for i in 101 .. 106 {
v.push(i.to_string());
}
// 从向量中随机抽取元素
let third = &v[2]; // 错误:不能移动到 Vec 索引结构之外<sup><b>3</b></sup>
let fifth = &v[4]; // 这里也一样
Copy
struct Label { number: u32 }
fn print(l: Label) { println!("STAMP: {}", l.number); }
let l = Label { number: 3 };
print(l);
println!("My label number is: {}", l.number);
**在 **println!
时发生了异常,因为在 print(l)
中已经将所有权交给了 print(l)
所有这样会提示异常
#[derive(Copy, Clone)]
struct StringLabel { name: String }
这样处理必须全部是 Copy 类型 Copy 类型可以查看附录
Rc
与 Arc
共享所有权
Rc
类型和 Arc
很像但是 Arc
是一个线程安全的类型.但是 Arc
性能比 Rc
性能差.
use std::rc::Rc;
// Rust能推断出所有这些类型,这里写出它们只是为了讲解时清晰
let s: Rc<String> = Rc::new("shirataki".to_string());
let t: Rc<String> = s.clone();
let u: Rc<String> = s.clone();
Rc
的指针是一个不可变的对象.
附录
Copy类型
- 基本标量类型
- 整数类型(如 i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize)
- 浮点数类型(如 f32, f64)
- 字符类型(char)
- 布尔类型(bool)
- 指针类型
- 原生指针(如 *const T, *mut T)
- 函数指针(如 fn())
- 元组(Tuple)
- 所有元素均为 Copy 类型的元组,如 (i32, i32)。但 (i32, String) 不是 Copy 类型,因为 String 不是 Copy 类型。
评论