【Rust知识】Rust 进阶知识

1. 生命周期

在任何语言里,栈上的值都有自己的生命周期,它和帧的生命周期一致,而 Rust,进一步明确这个概念,并且为堆上的内存也引入了生命周期。

  • 一般来说,堆内存的生命周期,会默认和其栈内存的生命周期绑定在一起。
  • 默认情况下,在每个函数的作用域中,编译器就可以对比值和其引用的生命周期,来确保"引用的生命周期不超出值的生命周期"。

值的生命周期

静态生命周期

如果一个值的生命周期贯穿整个进程的生命周期 ,那么我们就称这种生命周期为静态生命周期

当值拥有静态生命周期,其引用也具有静态生命周期。比如: &'static str 代表这是一个具有静态生命周期的字符串 引用。

一般来说,全局变量、静态变量、字符串字面量(string literal)等,都拥有静态生命周 期。我们上文中提到的堆内存,如果使用了 Box::leak 后,也具有静态生命周期。

动态生命周期

如果一个值是在某个作用域中定义的 ,也就是说它被创建在栈上或者堆上,那么其生命周期是动态的

当这个值的作用域结束时,值的生命周期也随之结束。对于动态生命周期,我们约定用 'a 、'b 或者 'hello 这样的小写字符或者字符串来表述。 ' 后面具体是什么名字不重要, 它代表某一段动态的生命周期,其中, &'a str 和 &'b str 表示这两个字符串引用的生 命周期可能不一致。

当出现了多个参数,它们的生命周期可能不一致时,返回值的生命周期就不好确定了。编译器在编译某个函数时,并不知道这个函数将来有谁调用、怎么调用,所以,函数本身携带的信息,就是编译器在编译时使用的全部信息

此时,就需要我们在函数签名中提供生命周期的信息,也就是生命周期标注(lifetime specifier)。在生命周期标注时,使用的参数叫生命周期参数(lifetime parameter)。 通过生命周期标注,我们告诉编译器这些引用间生命周期的约束。

生命周期参数的描述方式和泛型参数一致,不过只使用小写字母。这里,两个入参 s1、 s2,以及返回值都用 'a 来约束。生命周期参数,描述的是参数和参数之间、参数和返回 值之间的关系,并不改变原有的生命周期

总结

  1. 所有引用类型的参数都有独立的生命周期 'a 、'b 等。
  2. 如果只有一个引用型输入,它的生命周期会赋给所有输出。
  3. 如果有多个引用类型的参数,其中一个是 self,那么它的生命周期会赋给所有输出。
  4. 使用数据结构时,数据结构自身的生命周期,需要小于等于其内部字段的所有引用的生命周期。

2. 内存管理

Rust 的创造者们,重新审视了堆内存的生命周期,发现大部分堆内存的需求在于动态大 小,小部分需求是更长的生命周期。

值的创建

struct

Rust 在内存中排布数据时,会根据每个域的对齐(aligment)对数据进行重排,使其内存大小和访问效率最好。

如果结构体的定义考虑地不够周全,会为了对齐浪费很多空间。

enum

enum是一个标签联合体(tagged union),它的大小是标 签的大小,加上最大类型的长度。定义 enum 数据结构时,有 Option 和 Result<T, E> 两种设计举例。Rust 编译器会对 enum 做一些额外的优化,让某些常用结构的内存布局更紧凑。引用类型的第一个域是个指针,而指针是不可能等于 0 的,但是我们可以复用这个指针:当其为 0 时,表示 None,否则是 Some,减少了内存占用

vec 和 String

String 结构内部就是一个 Vec;Vec 结构是 3 个 word 的胖指针,包含:一个指向堆内存的指针 pointer、分配的堆内存的容量 capacity,以及数据在堆内存的长度 length。

很多动态大小的数据结构,在创建时都有类似的内存布局:栈内存放的胖指针,指向堆内 存分配出来的数据

值的使用

一个值如果没有实现 Copy,在赋值、传 参以及函数返回时会被 Move。其实 Copy 和 Move 在内部实现上,都是浅层的按位做内存复制,只不过 Copy 允许你访 问之前的变量,而 Move 不允许。

内存复制是个很重的操作,效率很低。但是,如果你要复制的只是原生类型(Copy)或者栈上的胖指针(Move),不涉及堆内 存的复制也就是深拷贝(deep copy),那这个效率是非常高的,我们不必担心每次赋值 或者每次传参带来的性能损失。所以,无论是 Copy 还是 Move,它的效率都是非常高的。

在使用值的过程中,除了 Move,你还需要注意值的动态增长。因为 Rust 下,集合类型的数据结构,例如:Vec,都会在使用过程中自动扩展。但只使用了很小部分的容量,内存的使用效率很低,所以你要考虑使用,比如 shrink_to_fit 方法,来节约对内存的使用。

值的销毁

当一个值要被释 放,它的 Drop trait 会被调用。如果要释放的值是一个复杂的数据结构,比如一个结构体,那么这个结构体在调用 drop() 时,会依次调用每一个域的 drop() 函数,如果域又是一个复杂的结构或者集合类型,就会 递归下去,直到每一个域都释放干净。整个释放顺序从内到外是:先释放 HashMap 下的 key,然后释放 HashMap 堆上的表结构,最后释放栈上的内存

3. 类型系统

类型系统其实就是,对类型进行定义、检查和处理的系统。

生命周期标注也是泛型的一部分,一个生命周期 'a 代表任意 的生命周期,和 T 代表任意类型是一样的。

相关推荐
姜学迁17 小时前
Rust-枚举
开发语言·后端·rust
凌云行者17 小时前
rust的迭代器方法——collect
开发语言·rust
QMCY_jason1 天前
Ubuntu 安装RUST
linux·ubuntu·rust
碳苯1 天前
【rCore OS 开源操作系统】Rust 枚举与模式匹配
开发语言·人工智能·后端·rust·操作系统·os
zaim11 天前
计算机的错误计算(一百一十四)
java·c++·python·rust·go·c·多项式
凌云行者2 天前
使用rust写一个Web服务器——单线程版本
服务器·前端·rust
cyz1410012 天前
vue3+vite@4+ts+elementplus创建项目详解
开发语言·后端·rust
超人不怕冷2 天前
[rust]多线程通信之通道
rust
逢生博客2 天前
Rust 语言开发 ESP32C3 并在 Wokwi 电子模拟器上运行(esp-hal 非标准库、LCD1602、I2C)
开发语言·后端·嵌入式硬件·rust
Maer092 天前
WSL (Linux)配置 Rust 开发调试环境
linux·运维·rust