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 类型。
评论