Rust 入门 所有权系统

所有权

所有权是 Rust 最为与众不同的特性,它让 Rust 无需垃圾回收(garbage collector)即可保障内存安全。因此,理解 Rust 中所有权如何工作是十分重要的。

什么是所有权

  • Rust 的核心特性就是所有权
  • 所有程序在运行时都必须管理程序所使用的内存
    • 有些语言采用垃圾回器来保证内存安全
    • 还有一部分语言需要程序员来管理内存
  • Rust 是如何管理内存的?
    • Rust 通过所有权系统来管理内存
    • 通过所有权系统管理内存, 编译器在编译时会根据一系列的规则进行检查
    • 当程序运行时, 所有权系统不会减慢程序运行的速度

栈(Stack) 与 堆(Heap)

  • 栈和堆都是代码在运行时可供使用的内存, 但是它们的结构不同
  • 值是位于栈上还是堆上影响了程序运行的速度
  • 堆与栈更大程度上影响了语言的行为以及为何必须做出这样的抉择

栈(Stack)

  • 栈以放入值的顺序存储值并以相反顺序取出值
  • 这也被称作 后进先出(last in, first out)
  • 不能从中间也不能从底部插入或者取出数据
  • 增加数据叫做 进栈(pushing onto the stack)
  • 而移出数据叫做 出栈(popping off the stack)
  • 栈中的所有数据都必须占用已知且固定的大小

堆(Heap)

  • 在编译时大小未知或大小可能变化的数据, 要存储在堆上
  • 向堆存入数据时你要请求一定大小的空间
  • 操作系统在堆的某处找到一块足够大的空位, 把它标记为已使用, 并返回一个表示该位置地址的 指针(pointer) , 这个过程称作 在堆上分配内存(allocating on the heap), 简称为 分配(allocating)
  • 将数据推入栈中并不被认为是分配, 因为指针的大小是已知并且固定的, 你可以将指针存储在栈上, 当需要实际数据时, 必须访问指针指向的堆空间

认识所有权

所有权系统解决的问题

  • 跟踪代码的哪些部分正在使用堆中的哪些数据
  • 最小化堆中的重复数据
  • 清理堆上未使用的数据避免内存空间不足
  • 了解堆栈有助于理解所有权系统

所有权规则

  • Rust 中的每一个值都有一个被称为其 所有者(owner) 的变量
  • 值在任一时刻有且只有一个所有者
  • 当所有者超出 作用域(scope), 这个值将被丢弃

变量的作用域指的是变量的有效范围, 当变量超出这个范围后, 值不再有效

内存和分配

  • 首先我们需要选择一种存储在堆上的数据类型作为我们的研究对象, String这个类型就非常合适

  • 我们通过 String::from() 函数将一个字符串字面值转换为一个字符串变量, 此时操作系统会分配内存给我们的示例程序

  • 当这个字符串变量离开有效作用域时, Rust 程序会主动释放这部分内存, 不需要手动管理

    • C/C++需要手动释放内存:
      • 如果忘了手动释放内存, 就会造成内存泄漏
      • 如果提前释放了内存, 就会造成悬垂指针
      • 如果释放了两次, 就会造成数据丢失或者其他问题
    • Java 拥有GC垃圾回收器:
      • GC会跟踪并识别内存, 当一段内存不再使用时, GC线程会主动释放内存
  • 在 Rust 中, 对于某个值来说, 当拥有此值的变量超出作用域时, 内存就会立刻释放

例子

fn main() {
    let s1 = String::form("Hello"); // 给s1 赋值
    
    let s2 = s1; // s1将所有权移交给了s2, 可以理解为将 s1 的值移动到 s2 中
    // 此时如果我们使用s1这个变量, 就会报错
    //println!("{}", s1);
    
    // 如果我们需要两份相同的数据进行操作, 只能进行拷贝值
    let s3 = s2.clone();
}

所有权与函数

  • 将一个值传递给函数会发生移动或者复制
fn main() {
    let i = 1;
    func_1(i); // i32类型的存储位置是栈, 属于简单类型, 所以值被复制
    
    let s = String::from("Hello");
    func_2(s); // String类型的储存位置是堆, 属于复杂类型, 所以值被移动, 也就是所有权发生了转移
}

fn func_1(value: i32) {
    println!("{}", value);
}

fn func_1(value: String) {
    println!("{}", value);
}
  • 函数在返回一个复杂类型的值时, 也会发生所有权的转移
fn main() {
    let value = get_value();
    println!("{}", value);
    
    // value的所有权被移动到了func函数内
    // func函数将value的所有权移动给了返回值
    // 返回值的所有权被移动给了value2
    let value2 = func(value);
    println!("{}", value);	// 此行报错, value的值被移动到了value2中, 也就是所有权发生了转移
    println!("{}", value2);
}

fn get_value() -> String {
    let value = String::from("Hello");
    return value;
}

fn func(value: String) -> String {
    return value;
}
  • 把一个值赋给其他变量时, 所有权会发生转移
  • 当一个包含 堆空间 数据的变量离开作用域时, 他的值会被drop函数清理, 除非所有权发生移动
如果对我分享的内容感兴趣的话  记得关注我得动态
求推荐  求收藏  求转发  求关注
posted @ 2021-06-21 19:09  Delayer  阅读(153)  评论(0编辑  收藏  举报