如同泛型一般,生命周期也可以使用边界。符号“:”在这有不同的意义,但是符号“+”是相同的。注意下面的要点:
- T: ‘a:所有关于T的引用必须比“’a”的生命周期更长。
- T: Trait + ‘a:类型T必须实现接口Trait,所有关于T的引用都要比 ’a的生命周期更长。
下面的示例展示了上面要点中的语法在关键字where之后使用方法:
use std::fmt::Debug; // Trait to bound with.
#[derive(Debug)]
struct Ref<'a, T: 'a>(&'a T);
// `Ref` contains a reference to a generic type `T` that has
// some lifetime `'a` unknown by `Ref`. `T` is bounded such that any
// *references* in `T` must outlive `'a`. Additionally, the lifetime
// of `Ref` may not exceed `'a`.
// A generic function which prints using the `Debug` trait.
fn print<T>(t: T) where
T: Debug {
println!("`print`: t is {:?}", t);
}
// Here a reference to `T` is taken where `T` implements
// `Debug` and all *references* in `T` outlive `'a`. In
// addition, `'a` must outlive the function.
fn print_ref<'a, T>(t: &'a T) where
T: Debug + 'a {
println!("`print_ref`: t is {:?}", t);
}
fn main() {
let x = 7;
let ref_x = Ref(&x);
print_ref(&ref_x);
print(ref_x);
}
代码解析
// 语法类比:
// 泛型约束 T: Trait
// 生命周期约束 T: 'a
// 混合约束 T: Trait + 'a
带有生命周期的泛型结构体
#[derive(Debug)]
struct Ref<'a, T: 'a>(&'a T);
// 解读:
// 1. `T: 'a` 表示类型T中的所有引用必须比`'a`存活更久
// 2. 结构体本身不能超过`'a`的生命周期
// 效果:确保被引用的数据比结构体实例存活更久
泛型函数的基本约束
fn print<T>(t: T) where
T: Debug {
println!("`print`: t is {:?}", t);
}
// 普通泛型约束:只要求实现Debug trait
带有生命周期约束的引用函数
fn print_ref<'a, T>(t: &'a T) where
T: Debug + 'a {
println!("`print_ref`: t is {:?}", t);
}
// 双重约束:
// 1. T: Debug —— 必须实现Debug trait
// 2. T: 'a —— T中的所有引用必须比`'a`存活更久
// 额外保证:函数不能超过`'a`的生命周期
主函数解析
fn main() {
let x = 7; // 数据生命周期开始
let ref_x = Ref(&x); // 创建引用包装
print_ref(&ref_x); // 合法:x和ref_x都满足生命周期要求
print(ref_x); // 转移所有权到print函数
}
// 关键点:x的生命周期包含所有使用场景
生命周期边界的三层含义
- T: ‘a的深层含义
当T是引用类型时:直接要求该引用比‘’a’长。
当T包含嵌套引用时:所有层级的引用都必须比‘’a’长。
当T是值类型时:自动满足(因不包含引用)
- 与泛型约束的结合
fn example<'a, T>(t: &'a T) where
T: Debug + 'a + Send
// 解读:
// 1. 必须实现Debug和Send trait
// 2. 所有内部引用必须比`'a`长
- where 子句的优势
提高复杂约束的可读性
允许将约束与函数签名分离
为什么需要这种设计
防止结构体有无效引用
struct Invalid<'a> {
data: &'a str // 必须保证被引用的字符串比结构体存活久
}
支持嵌套引用场景
struct Complex<'a> {
inner: &'a Vec<&'a String> // 字符串和Vec都必须比'a长
}
保证trait对象的可靠性
trait Trait {}
fn make_object<'a>(t: &'a (dyn Trait + 'a))
// 确保trait对象内部引用有效
实际编译器的处理
当使用下面的代码:
fn print_ref<'a, T>(t: &'a T) where T: 'a
编译器处理流程:
- 检查传入的&’a T。
- 递归分析T类型中的所有引用。
- 验证每个引用是否确实比’a存活更久
如果发现任何引用比’a存活时间短,立即报错。