正式开始
在 Rust 中,除非显式地做 Box::leak() / Box::into_raw() / ManualDrop 等动作,一般来说,堆内存的生命周期,会默认和其栈内存的生命周期绑定在一起
值的生命周期
-
静态生命周期:一个值的生命周期贯穿整个进程的生命周期
a. 当值拥有静态生命周期,其引用也具有静态生命周期,可以用 'static 来表示
b. &'static str 代表这是一个具有静态生命周期的字符串不可变借用
c. 一般来说,全局变量、静态变量、字符串字面量(string literal)等,都拥有静态生命周期
-
动态生命周期:如果一个值是在某个作用域中定义的,也就是说它被创建在栈上或者堆上,那么其生命周期是动态的
a. 当这个值的作用域结束时,值的生命周期也随之结束
b. 对于动态生命周期,我们约定用 'a 、'b 或者 'hello 这样的小写字符或者字符串来表述。 ' 后面具体是什么名字不重要,它代表某一段动态的生命周期
小结
- 分配在堆和栈上的内存有其各自的作用域,它们的生命周期是动态的。
- 全局变量、静态变量、字符串字面量、代码等内容,在编译时会被编译到可执行文件的 BSS/Data/RoData/Text 段,然后在加载时装入内存。它们的生命周期和进程的生命周期一致,是静态的。
- 函数指针的生命周期也是静态的,因为函数在 Text 段中,只要进程活着,其内存一直存在
编译器如何识别生命周期
生命周期标注的目的是,在参数和返回值之间建立联系或者约束。调用函数时,传入的参数的生命周期需要大于等于(outlive)标注的生命周期
- 函数本身携带的信息,就是编译器在编译时使用的全部信息
- 需要我们在函数签名中提供生命周期的信息,也就是生命周期标注(lifetime specifier)
- 在生命周期标注时,使用的参数叫生命周期参数(lifetime parameter)
- 通过生命周期标注,我们告诉编译器这些引用间生命周期的约束
- 生命周期参数的描述方式和泛型参数一致,不过只使用小写字母
- 生命周期参数,描述的是参数和参数之间、参数和返回值之间的关系,并不改变原有的生命周期
编译器自动添加生命周期的规则
所有使用了引用的函数,都需要生命周期的标注
- 所有引用类型的参数都有独立的生命周期 'a 、'b 等。
- 如果只有一个引用型输入,它的生命周期会赋给所有输出。
- 如果有多个引用类型的参数,其中一个是 self,那么它的生命周期会赋给所有输出。
编译器如何检查
- 当每个函数都添加好生命周期标注后,编译器就可以从函数调用的上下文中分析出,在传参时引用的生命周期是否和函数签名中要求的生命周期匹配。
- 如果不匹配,就违背了“引用的生命周期不能超出值的生命周期”,编译器就会报错
小结
-
根据所有权规则,值的生命周期可以确认,它可以一直存活到所有者离开作用域
-
引用的生命周期不能超过值的生命周期。在同一个作用域下,这是显而易见的。然而,当发生函数调用时,编译器需要通过函数的签名来确定,参数和返回值之间生命周期的约束。
a. 大多数情况下,编译器可以通过上下文中的规则,自动添加生命周期的约束
b. 如果无法自动添加,则需要开发者手工来添加约束
1. 一般,我们只需要确定好返回值和哪个参数的生命周期相关就可以了 2. 对于数据结构,当内部有引用时,我们需要为引用标注生命周期
好的链接
精选问答
-
对&str这个类型的理解?
a. &str是一个字符串切片,一个带有长度的胖指针,指向字符串实际的位置
b. 它可以指向 "hello world",此时指针指到了 RODATA/STRING section 里 "hello" 的地址,它的生命周期是 'static
c. 也可以指向 "hello world".to_string(),此时指针指向了这个字符串的堆地址,生命周期是 'a。
-
对生命周期检查的一点思考
a. 在含有引用的函数调用中,编译器会尝试根据规则进行补齐,如果无法自动补齐,就会要求开发者进行标注
b. 开发者标注的生命周期会在两个地方生效
1. 函数的实现中,会去校验标注的正确性 2. 在函数的调用点也会根据函数声明中的标注,对入参和返回值进行检查c. 函数声明中的生命周期标注,其实就是同时约束实现方和调用方的约定。在标注的约束关系中,如果检查发现调用方和实现方都满足约束,则编译通过
-
”&mut &str 添加生命周期后变成 &'b mut &'a str“,为什么编译器会自动标注成这样?不是一个参数一个生命周期吗?
a. 一个引用一个生命周期
-
某个函数的参数生命周期是变化的,可能是<'a,'a>也可能是<'a,'b>,这样的话函数要实现2遍吗
a. 'a 代表的是一个泛化的生命周期,表示我支持任何长度的生命周期
比如
fn max<'a, 'b: 'a>(s1: &'a str, s2: &'b str) -> &'a str {}b. 表明 'a 和 'b 可能是不同的生命周期,'b 生命周期大于等于 'ac. 返回值需要满足 'a 的生命周期。整个函数相当于在说,给定两个参数,返回值的生命周期要大于这两个参数中小的那个
d. 所以函数参数的生命周期是一种约束,而不是一个具体的值。它本身就可以随着传入的生命周期而自适应,只要它们满足约束就可以