生命周期
- Rust 中每一个引用都有其生命周期,也就是引用保持有效的作用域。大部分时候生命周期都是隐含并可以推断的,有点类似 rust 的类型推断。
- 生命周期的主要目标是避免悬垂指针,这点应该 rust 优势吧,可以将一些
- Rust 编译器使用借用检查器来检查生命周期是否有效。
fn main(){
let r;
{
let x = 5;
r = &x;
}
println!("r = {}",r);
}
Rust 之所以大家愿意放弃 cpp 而投身到 rust 的原因就是 rust 能够兼顾效率和安全。安全是在编译过程检查。
|
5 | r = &x;
| ^^^^^^ borrowed value does not live long enough
6 | }
| - `x` dropped here while still borrowed
7 |
8 | println!("r = {}",r);
| - borrow later used here
这个应该不难理解,r 是指向 x 的内存地址的引用,x 在离开作用域 会 drop 掉 x 内存,也就是释放 r 所引用的内存,所以当离开。
函数中生命周期
fn longest(x:&str,y:&str) -> &str {
if x.len() > y.len(){
x
}else{
y
}
}
--> src/main.rs:1:30
|
1 | fn longest(x:&str,y:&str) -> &str {
| ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
传入 x 和 y 都是引用,返回也是引用,为了保证返回的引用不是悬垂指针,所以告诉编译器生命周期需要满足参数的生命周期要大于返回值生命周期。
fn longest<'a>(x:&'a str,y:&'a str) -> &'a str {
if x.len() > y.len(){
x
}else{
y
}
}
fn main(){
let s1 = String::from("hello");
let s2 = String::from("world");
let res = longest(s1.as_str(), s2.as_str());
println!("res = {}",res);
}
看起来有点特殊,声明一下生命周期。这也就是说明 rust 在编译过程的安全性。
fn get_str<'a>(x: &'a str, y: &str) -> &'a str{
x
}
因为 get_str
返回值 str zhi'y
fn get_str_one<'a>(x:&'a str,y:&'a str) -> &'a str{
let r = String::from("hello");
r.as_str()
}
结构体声明周期
#[derive(Debug)]
struct Tut {
title: &str,
}
#[derive(Debug)]
struct Tut<'a> {
title: &'a str,
}
#[derive(Debug)]
struct Tut<'a> {
title: &'a str,
}
fn main(){
let s1 = String::from("hello");
let s2 = String::from("world");
let res = longest(s1.as_str(), s2.as_str());
println!("res = {}",res);
let vue = String::from("vue");
let vueTut = Tut{title:&vue};
println!("a = {:#?}",vueTut)
}
生命周期省略
- 没有生命周期注解却能够编译,rust 团队将很明确的模式进行简化即使不声明生命周期
fn get_str_two(s:&str) -> &str{
s
}
-
编译器会根据可以省略生命周期声明的 3条规则来推断生命周期,如果不满足就会要求开放人员指定生命周期给出错误提示,还要补充说明一点就是生命周期注解省略规则适用于 fn 定义和 impl 块定义。
开始介绍 3 个省略原则前,介绍用于理解规则的两个概念输入生命周期和输出生命周期,函数或方法的参数的生命周期称为输入生命周期。而返回值的生命周期称为输出生命周期。- 每一个引用的参数都有自己生命周期参数
一个引用参数函数,fn foo<'a>(x:&'a u32)
两个引用参数的函数fn bar<'a,'b>(x: &'a u32, y:&'b u32)
- 如果只有一个输入生命周期参数,那么被赋予所有输出生命周期参数
fn foo(x:&i32)->&i32
就等价于fn<'a>(x:&a' i32)-> &'a i32
- 如果方法有多个输入生命周期参数,不过其中之一因为方法的缘故为&self或者&mut self 那么self 的生命周期被赋予所有输出生命周期参数,例子在下面来看
- 每一个引用的参数都有自己生命周期参数
#[derive(Debug)]
struct Tut<'a> {
title: &'a str,
}
impl<'a> Tut<'a>{
fn do_something(&self)->i32{
3
}
}
fn main(){
let react = String::from("react");
let reactTut = Tut{title:&react};
println!("{}",reactTut.do_something());
}
在 do_something
方法里没有声明生命周期,
impl<'a> Tut<'a>{
fn do_something(&self)->i32{
3
}
fn do_something_1(&self,s:&str)->&str{
self.title
}
fn do_something_2(&self,s:&str)->&str{
s
}
}
impl<'a> Tut<'a>{
fn do_something(&self)->i32{
3
}
fn do_something_1(&self,s:&str)->&str{
self.title
}
fn do_something_2<'b>(&self,s:&'b str)->&'b str{
s
}
}
静态生命周期
定义方式'static
,其生命周期存活于整个程序期间,所有的字符字面值都拥有static 生命周期。有点类似全局变量。
use std::fmt::Display;
fn func<'a,T:Display>(x:&'a str,y:&'a str, ann:T)-> &'a str{
println!("ann is {}",ann);
if x.len() < y.len(){
x
}else{
y
}
}
fn func<'a,T:Display>(x:&'a str,y:&'a str, ann:T)-> &'a str{
println!("ann is {}",ann);
if x.len() < y.len(){
x
}else{
y
}
}
let str1 = String::from("vue");
let str2 = String::from("react");
let ann = 129;
let res = func(str1.as_str(), str2.as_str(), ann);