深入了解Rust的生命周期
作者:闲鱼翻不了身
Rust生命周期简介
Rust 中的每一个引用都有其 生命周期(lifetime),也就是引用保持有效的作用域。
生命周期的主要目标是避免悬垂引用,它会导致程序引用了非预期引用的数据。
{ let r; { let x = 5; r = &x; } //x出作用域后已经被删除,发生悬垂引用 println!("r: {}", r); }
实现生命周期检查的手段:Rust 编译器有一个 借用检查器(borrow checker),它比较作用域来确保所有的借用都是有效的。
//函数中的泛型生命周期 fn longest(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } } //该函数会发生编译错误,因为 Rust 并不知道将要返回的引用是指向 x 或 y //当我们定义这个函数的时候,并不知道传递给函数的具体值,所以也不知道到底是 if 还是 else 会被执行。 //我们也不知道传入的引用的具体生命周期,所以也就不能通过观察作用域来确定返回的引用是否总是有效。 //借用检查器自身同样也无法确定,因为它不知道 x 和 y 的生命周期是如何与返回值的生命周期相关联的。
生命周期标注语法:
生命周期语法是用于将函数的多个参数与其返回值的生命周期进行关联的。一旦他们形成了某种关联,Rust 就有了足够的信息来允许内存安全的操作并阻止会产生悬垂指针亦或是违反内存安全的行为。
Q:为什么有这个语法?
A:因为编译器笨,借用检查器不知道类似上述情况中的生命周期怎么比较,所以靠人手写给他约束。
生命周期标注并不改变任何引用的生命周期的长短。与当函数签名中指定了泛型类型参数后就可以接受任何类型一样,当指定了泛型生命周期后函数也能接受任何生命周期的引用。生命周期标注描述了多个引用生命周期相互的关系,而不影响其生命周期。
生命周期标注有着一个不太常见的语法:生命周期参数名称必须以撇号('
)开头,其名称通常全是小写,类似于泛型其名称非常短。'a
是大多数人默认使用的名称。生命周期参数标注位于引用的 &
之后,并有一个空格来将引用类型与生命周期标注分隔开。
//使用泛型生命周期标注语法解决上述问题 fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } //现在函数签名表明对于某些生命周期 'a,函数会获取两个参数,他们都是与生命周期 'a 存在的一样长的字符串 slice。 //函数会返回一个同样也与生命周期 'a 存在的一样长的字符串 slice。 //它的实际含义是 longest 函数返回的引用的生命周期与传入该函数的引用的生命周期的较小者一致。 //这就是我们告诉 Rust 需要其保证的约束条件。 //两个直观的例子 //1.正确示例:返回的引用生命周期与较小的string2一致,函数调用正确 fn main() { let string1 = String::from("long string is long"); { let string2 = String::from("xyz"); let result = longest(string1.as_str(), string2.as_str()); println!("The longest string is {}", result); } } //2.错误示例:返回的引用生命周期与string2一致,但是println时,result引用生命周期已经结束,编译失败 fn main() { let string1 = String::from("long string is long"); let result; { let string2 = String::from("xyz"); result = longest(string1.as_str(), string2.as_str()); } println!("The longest string is {}", result); }
类似的,结构体,方法中使用到引用时也可以使用生命周期标注语法。
题外话:Rust观法有在尽力的减轻开发者负担,一些很常见的需要生命周期标注的情况,编译器已经在内部实现好了,未来用户只会越来越少的使用到生命周期标注语法。被Rust 官方考虑到的一些引用分析的模式被称为 生命周期省略规则(lifetime elision rules)。
比较特殊的,'static
,其生命周期能够存活于整个程序期间。所有的字符串字面量都拥有 'static
生命周期。
//static生命周期 let s: &'static str = "I have a static lifetime.";
总结:
生命周期概念和大多数语言一样,但Rust对变量生命周期的检查是很具有特色的,还有他特别的生命周期标注语法是很性新颖的,能在编译期处理的错误绝不留到运行时这是Rust很棒的设计理念.
到此这篇关于深入了解Rust的生命周期的文章就介绍到这了,更多相关Rust生命周期内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!