Rust 所有权与移动

谈及内存管理,我们希望编程语言能具备两个特点:

  1. (控制优先)希望内存可以在我们选定的时机进行释放, 这使我们能控制程序的内存消耗;
  2. (安全优先)在对象被释放后,我们绝不希望继续使用指向它的指针,这是未定义行为,会导致崩溃和安全漏洞。

所有权

可以理解为这个参数的拥有者是谁,每个变量都将只会有一个拥有者.

变量拥有自己的值,当控制流离开声明变量的块时,变量就会被丢弃,因此它的值也会一起被丢弃。

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 类型可以查看附录

RcArc 共享所有权

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类型

  1. 基本标量类型
    1. 整数类型(如 i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize)
    2. 浮点数类型(如 f32, f64)
    3. 字符类型(char)
    4. 布尔类型(bool)
  2. 指针类型
    1. 原生指针(如 *const T, *mut T)
    2. 函数指针(如 fn())
  3. 元组(Tuple)
    1. 所有元素均为 Copy 类型的元组,如 (i32, i32)。但 (i32, String) 不是 Copy 类型,因为 String 不是 Copy 类型。