Rust 生命周期

本文深入探讨了Rust中引用的生命周期概念,包括其作用域、生命周期检查以及如何通过生命周期参数显式声明。主要内容包括隐式和显式生命周期、函数参数与返回值的生命周期关联,以及结构体中的生命周期。特别强调了生命周期对于防止悬垂引用和确保内存安全的重要性,并举例说明了错误的生命周期可能导致的问题及解决方案。
摘要由CSDN通过智能技术生成

Rust 生命周期

首先每个引用都有生命周期,也就是引用保持有效的作用域

一个引用的作用域从声明的地方开始一直持续到最后一次使用为止

let a=String::from("a");
let b=&a;//b的诞生 ,后续没有在使用b,所以b死亡

在借用者生命期间,所有者必须是活的,不然就会产生悬垂引用,幸运的是我们不用关注它,交给编译器来提示,编译器通过生命周期来检查
大部分时候生命周期是隐含并可以推断的,但有些情况就无法推断了,需要程序员自己指出

fn longest(x: &String, y: &String) -> &String {//这个函数会报错,具体原因我们后面会讲到
	if x.len() > y.len() {//可以理解成随机返回 x 或 y,因为在运行时2种情况都会出现
		x
	} else { 
		y 
	}
}
fn main() {
	let a=String::from("a");
	let c;
	{
		let b=String::from("b");
		c = longest(&a,&b);//我们并不知道它会返回a还是b,这导致生命周期的不确定性,那么此时c就是不安全的,你不敢在大括号外使用c
	}
}

下面是修改后

fn longest<'a>(x: &'a String, y: &'a String) -> &'a String {//统一生命周期,按照最小生命周期来分析
	if x.len() > y.len() {
		x
	} else {
		y
	}
}
fn main() {
	let a=String::from("a");
	let c;
	{
		let b=String::from("b");
		c = longest(&a,&b);
		println!("{}",c);//安全
	}//b在这里就死了
	// println!("{}",c);//这行会报错,因为最小生命周期是b
}

注意:生命周期声明类似于变量类型声明,不会改变对象的真正生命周期。当你生命的生命周期和实际不符合的时候,编译器会报错。

函数或方法的参数的生命周期被称为 输入生命周期,而返回值的生命周期被称为 输出生命周期
隐式生命周期,官方介绍了3条规则

  1. 每一个是引用的参数都有它自己的生命周期参数
  2. 如果只有一个输入生命周期参数,那么它被赋予所有输出生命周期参数
  3. 如果方法有多个输入生命周期参数并且其中一个参数是 &self 或 &mut self, 那么所有输出生命周期参数被赋予 self 的生命周期

你应该意识到了,其实都是隐式存在生命周期的,上面只不过是编辑器无法分析,要求我们显式声明

fn f(x:&i32){}
fn f<'a>(&'a i32){}

fn f2(x:&String)->&String{}
fn f2<'a>(x:&'a String)->&'a String{}
//为什么返回值一定是'a ,因为如果是函数内部的所有者,那么返回出去的借用者就是 悬垂引用,因为在退出函数时 那个所有者就死了
//所以当 参数们只有一个生命周期时,那么返回值也一定是那个生命周期

fn f3(x:&i32,y:&i32,...){}
fn f3<'a,'b,...>(x:&'a i32,y:&'b i32,...){}
//再多参数也是这样,没必要显式声明,咱就懒点

fn f4<'a,'b>(x:&'a String,y:&'b String) -> &'a String {
	// &String::from("") //报错,生命周期不是'a
	// &y	//报错,原因同上
	&x
}

关于第三条,特别说明一下

impl ABC {//如果ABC 是一个加工用的对象,那么就不应该返回&self的生命周期
	fn f1(&self,a:&String)->&String{//返回的生命周期是&self的
		a //报错因为生命周期不同
	}
	fn f2<'a>(&self,a:&'a String)->&'a String{//但是可以这样
		a
	}
}

生命周期语法是用于将函数的多个参数与其返回值的生命周期进行关联的。一旦他们形成了某种关联,Rust 就有了足够的信息来允许内存安全的操作并阻止会产生悬垂指针亦或是违反内存安全的行为

结构体定义

struct ABC<'a>{
	A:&'a i32
}
impl<'a> ABC<'a> {
	fn f1(x:&'a i32){}
	fn f2(&self,x:&'a i32){}
}
struct ABCD<'a>{//一样
	A:&'a i32,
	B:&'a i32
}
struct ABCDE<'a,'b>{//不一样
	A:&'a i32,
	B:&'b i32
}

静态生命周期

'static 其生命周期能够存活于整个程序期间

不过将引用指定为 'static 之前,思考一下这个引用是否真的在整个程序的生命周期里都有效。你也许要考虑是否希望它存在得这么久,即使这是可能的。大部分情况,代码中的问题是尝试创建一个悬垂引用或者可用的生命周期不匹配,请解决这些问题而不是指定一个 'static 的生命周期

白话说就是 它是否真的可以存活整个程序期间,不是靠'static,而是这个引用是否真实可以存活,前面提到:生命周期声明类似于变量类型声明,不会改变对象的真正生命周期,所以’'static’只是告诉编译器而已

所有的字符串字面值(&str)都拥有 'static 生命周期


生命周期声明是入参和返回值或者结构体成员之间的一种生命周期约定和限制

参考:
官方文档
知乎 - Rust生命周期

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艾仪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值