1. 自学 rust --- 所有权

1,086 阅读5分钟

学习资料

目前主要通过以下渠道来学习:

前两个是文档,最后一个是视频。 我自己感觉还是看视频爽,这个老师讲课一点不拖泥带水,很棒棒。视频有助于快速形成概念,文档适合反复查阅,重难点突破。 如果谁有更好的学习资源,分享给我!!!磕头。。。

所有权

现在已经学到所有权啦。数据类型、流程控制这些有编程基础的情况下很容易理解。 忘了说了,这文章我是打算当作自己的笔记来着。如果能对想学习 rust 的同学起到哪怕一点点作用。那真是善莫大焉。

为什么要有所有权

所有权是什么,为什么要引入所有权? 所有权是 rust 的内存管理方案。 目前市面上主要有三种内存管理方案。

  • GC 垃圾回收:Java、JavaScript、Go 等
  • 无 GC,手动回收:C、C++
  • 所有权:Rust

GC 解放了程序员,无需关注内存释放,程序运行时通过运行垃圾回收来检查未被回收的内存,并将其回收归还给操作系统。同时也带来了运行时开销。

手动回收则是完全信任程序员,需要程序员正确的跟踪每次内存使用,并在使用完后归还给操作系统,每次使用只能回收一次,不回收或者多次回收都会引发安全问题。 对程序员来说无疑增加了更多的心智负担。

Rust 走的是第三条路,即所有权。通过所有权机制,rust 编译器就做了垃圾回收,既避免了 GC 带来的额外开销,又对内存进行了跟踪和回收,将垃圾回收提前到编译时。

stack VS. heap

要讲清楚所有权,就要先明白栈内存和堆内存。 栈内存和堆内存是两种不同的内存组织方式。

栈内存

栈内存是栈结构的,后进先出,或者说先进后出。在栈中的数据大小是确定的。 程序执行时候会反复进行压栈、出栈。

堆内存

堆内存中的数据大小是不确定。

数据存储到栈内存上比存储在堆内存更高效,因为每次的存储地点都是栈顶。而堆内存每次需要系统分配足够的空间,并将相应的内存标记为已使用,同时返回该内存的指针。指针的大小是确定的,因此可以在栈内存中使用。

所有权以及垃圾回收就是针对堆内存的。

所有权要解决的问题

  1. 跟踪代码的那些部分正在使用 Heap 上的哪些数据
  2. 最小化 Heap 上的重复数据量
  3. 清理 Heap 上未使用的数据以避免空间问题

所有权规则

每个值有且只有一个对应的变量,该变量称为该值的所有者,在值的变量(所有者)离开作用域(scope)后,该值被删除,值所使用的内存立即交还给操作系统(rust 调用 drop 函数)。

存放在堆上的值的变量包含三部分:

  • ptr:指针
  • len:数据所占用的内存长度
  • capacity:申请到的内存容量

移动(move)

或者说转移,浅拷贝+之前的变量失效:

fn main(){
    let a = new String::from("hello");
    let b = a;
    println!("{}",a)
}

对于上面的代码,a 所指向的变量是 String 类型,存储在堆内存,所有权系统对其起作用,let b = a;的时候,发生 move,即值的所有权转移到 b,那么变量 a 就失效了,后面再引用 a 就会报错。

为什么要这么设计呢?a 不失效可以吗?如果 a 不失效,那么当 main 函数执行结束的时候,a 和 b 都离开了作用域,rust 会调用 drop 函数回收他们对应的堆内存,这样就会回收两次,会导致野指针等问题。

其实 a 也是可以不失效的,那就要用到 clone 函数了,clone 会完成深拷贝,即拷贝变量的ptr、len、capacity 信息的同时,将堆内存的数据也进行拷贝:

fn main(){
    let a = String::from("hello");
    let b = a.clone();
    println("{}",a);
}

由于用到了 clone 函数,上面的代码是不会报错的,println中引用 a 是可以的。

copy

上面讲到了,所有权系统主要针对的是堆内存上的数据,那么栈上的数据做相同的操作会如何呢?

fn main(){
    let a = 5;
    let b = a;
    println("{}",a);
}

答案是,不会报错,可以打印 a 的值。对于简单类型,或者说标量类型的值,直接进行复制开销也是很小的,因为它们的大小固定且不大。 语言层面,实现了 copy 这个 trait 的类型都会有以上效果,即旧变量不会失效。

上面还提到了 drop 函数,实现了 drop 函数的类型,rust 不允许它实现 copy trait。也很好理解,copy 对应存放在栈内存的数据,drop 对应存放在堆内存的数据。

trait暂时理解为接口。

实现了copy trait的类型

  1. 简单标量的组合类型

  2. 任何需要分配内存(堆内存)或某种资源的都是无 copy 的

  3. 拥有 copy trait 的类型:

    1. 整数
    2. bool
    3. char
    4. 浮点类型
    5. 所有字段可 copy 的元祖(tuple)

结尾

累了,码字也挺累人,今天的笔记就先到这。如果有错误的地方,希望大家多多指正。如果有好的学习资源或者学习方法,也请多多指教。我去看视频了。。。