Rust-借用和生命周期

这篇具有很好参考价值的文章主要介绍了Rust-借用和生命周期。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

生命周期

一个变量的生命周期就是它从创建到销毁的整个过程。其实我们在前面已经注意到了这样的现象:

Rust-借用和生命周期,Rust,rust,开发语言,后端
然而,如果一个变量永远只能有唯一一个入口可以访问的话,那就太难使用了。因此,所有权还可以借用。

借用

变量对其管理的内存拥有所有权。这个所有权不仅可以被转移(move),还可以被借用(borrow)。

借用指针的语法使用&符号或者&mut符号表示。前者表示只读借用,后者表示可读写借用。借用指针(borrow pointer)也可以称作“引用”(reference)。借用指针与普通指针的内部数据是一模一样的,唯一的区别是语义层面上的。它的作用是告诉编译器,它对指向的这块内存区域没有所有权。

Rust-借用和生命周期,Rust,rust,开发语言,后端
这里会出现编译错误,信息为“cannot borrow immutable borrowed content’*v’as mutable”。原因在于Vec::push函数。它的作用是对动态数组添加元素,它的签名是

pub fn push(&mut self,value:T)

它要求self参数是一个&mut Self类型。而我们给foo传递的参数是&Vec类型,因此会报错。修复方式如下:

Rust-借用和生命周期,Rust,rust,开发语言,后端
对于&mut型指针,请大家注意不要混淆它与变量绑定之间的语法。

如果mut修饰的是变量名,那么它代表这个变量可以被重新绑定;如果mut修饰的是“借用指针&”,那么它代表的是被指向的对象可以被修改。示例如下:
Rust-借用和生命周期,Rust,rust,开发语言,后端

借用规则

关于借用指针,有以下几个规则:

  • 借用指针不能比它指向的变量存在的时间更长。
  • &mut型借用只能指向本身具有mut修饰的变量,对于只读变量,不可以有&mut型借用。
  • &mut型借用指针存在的时候,被借用的变量本身会处于“冻结”状态。
  • 如果只有&型借用指针,那么能同时存在多个;如果存在&mut型借用指针,那么只能存在一个;如果同时有其他的&或者&mut型借用指针存在,那么会出现编译错误。

借用指针只能临时地拥有对这个变量读或写的权限,没有义务管理这个变量的生命周期。因此,借用指针的生命周期绝对不能大于它所引用的原来变量的生命周期,否则就是悬空指针,会导致内存不安全。示例如下:

Rust-借用和生命周期,Rust,rust,开发语言,后端
在这里给大家提个醒:一般情况下,函数参数使用引用传递的时候,不仅在函数声明这里要写上类型参数,在函数调用这里也要显式地使用引用运算符。

但是,有一个例外,那就是当参数为self &self &mut self等时,若使用小数点语法调用成员方法,在函数调用这里不能显式写出借用运算符。以常见的String类型来举例:

Rust-借用和生命周期,Rust,rust,开发语言,后端
在这个示例中,所有的函数调用都是同样的语法,比如x.len()、x.push(‘!’)、x.into_bytes()等,但它们背后对self参数的传递类型完全不同,因此也就出现了不同的语义。

这是需要提醒大家注意的地方。当然,如果我们使用统一的完整函数调用语法,那么所有的参数传递类型在调用端都是显式写出来的。

任何借用指针的存在,都会导致原来的变量被“冻结”(Frozen)。示例如下:

Rust-借用和生命周期,Rust,rust,开发语言,后端
编译结果为:

error:cannot assign to `x^because it is borrowed

因为p的存在,此时对x的改变被认为是非法的。

生命周期标记

对一个函数内部的生命周期进行分析,Rust编译器可以很好地解决。但是,当生命周期跨函数的时候,就需要一种特殊的生命周期标记符号了。

函数的生命周期标记

Rust-借用和生命周期,Rust,rust,开发语言,后端
生命周期符号使用单引号开头,后面跟一个合法的名字。生命周期标记和泛型类型参数是一样的,都需要先声明后使用。在上面这段代码中,尖括号里面的’a是声明一个生命周期参数,它在后面的参数和返回值中被使用。

前面提到的借用指针类型都有一个生命周期泛型参数,它们的完整写法应该是&'a T &'a mut T,只不过在做局部变量的时候,生命周期参数是可以省略的。

生命周期之间有重要的包含关系。如果生命周期’a比’b更长或相等,则记为’a : 'b,意思是’a至少不会比’b短,英语读做“lifetime a outlives lifetime b”。对于借用指针类型来说,如果&'a是合法的,那么’b作为’a的一部分,&'b也一定是合法的。

另外,'static是一个特殊的生命周期,它代表的是这个程序从开始到结束的整个阶段,所以它比其他任何生命周期都长。这意味着,任意一个生命周期’a都满足’static : 'a。

在上面这个例子中,如果我们把变量t的真实生命周期记为’t,那么这个生命周期’t实际上是变量t从“出生”到“死亡”的区间。在函数被调用的时候,它传人的实际参数是&t,它是指向t的引用。那么可以说,在调用的时候,这个泛型参数’a被实例化为了’t。根据函数签名,基于返回类型的生命周期与参数是一致的,可以推理出test函数的返回类型是&'t i32。

如果我们把x的生命周期记为’x。这条let x =text(&t);语句实际上是把&'t i32类型的变量赋值给&'x i32类型的变量。这个赋值是否合理呢?它应该是合理的。因为这两个生命周期的关系是’t: 'x。test返回的那个指针在’t这个生命周期范围内都是合法的,在一个被’t包围的更小范围的生命周期内,它当然也是合法的。所以,上面这个例子可以编译通过。

接下来,我们把上面这个例子稍作修改,让test函数有两个生命周期参数,其中一个给函数参数使用,另外一个给返回值使用:

Rust-借用和生命周期,Rust,rust,开发语言,后端
编译时果然出了问题,在&arg.member这一行,报了生命周期错误。这是为什么呢?因为这一行代码是把&'a i32类型赋值给&'b i32类型。

'a和’b有什么关系?答案是什么关系都没有。所以编译器觉得这个赋值是错误的。

怎么修复呢?指定’a:'b就可以了。'a比’b“活”得长,自然,&'a i32类型赋值给&'b i32类型是没问题的。

验证如下:

Rust-借用和生命周期,Rust,rust,开发语言,后端
经过这样的改写后,我们可以认为,在test函数被调用的时候,生命周期参数’a和’b被分别实例化为了’t和’x。

它们刚好满足了where条件中的’t:'x约束。而&arg.member这条表达式的类型是&'t i32,返回值要求的是&'x i32类型,可见这也是合法的。

所以test函数的生命周期检查可以通过。

上述示例是读者比较难理解的地方。以下两种写法都是可行的:

Rust-借用和生命周期,Rust,rust,开发语言,后端
这里的关键是,Rust的引用类型是支持“协变”的。

在编译器眼里,生命周期就是一个区间,生命周期参数就是一个普通的泛型参数,它可以被特化为某个具体的生命周期。

我们再看一个例子。它有两个引用参数,共享同一个生命周期标记:

Rust-借用和生命周期,Rust,rust,开发语言,后端
上述示例中,select这个函数引入了一个生命周期标记,两个参数以及返回值都是用的这个生命周期标记。同时我们注意到,在调用的时候,传递的实参其实是具备不同的生命周期的。

x的生命周期明显大于y的生命周期,&x可存活的范围要大于&y可存活的范围,我们把它们的实际生命周期分别记录为’x和’y。

select函数的形式参数要求的是同样的生命周期,而实际参数是两个不同生命周期的引用,这个类型之所以可以匹配成功,就是因为生命周期的协变特性。

编译器可以把&x和&y的生命周期都缩小到某个生命周期’a以内,且满足’x:‘a,'y:‘a。返回的selected变量具备’a生命周期,也并没有超过’x和’y的范围。所以,最终的生命周期检查可以通过。

类型的生命周期标记

如果自定义类型中有成员包含生命周期参数,那么这个自定义类型也必须有生命周期参数。示例如下:

Rust-借用和生命周期,Rust,rust,开发语言,后端
在使用impl的时候,也需要先声明再使用:
Rust-借用和生命周期,Rust,rust,开发语言,后端
impl后面的那个’t是用于声明生命周期参数的,后面的Test<'t>是在类型中使用这个参数。

如果有必要的话,方法中还能继续引入新的泛型参数。

如果在泛型约束中有where T:'a之类的条件,其意思是,类型T的所有生命周期参数必须大于等于’a。

要特别说明的是,若是有where T:'static的约束,意思则是,类型T里面不包含任何指向短生命周期的借用指针,意思是要么完全不包含任何借用,要么可以有指向’static的借用指针。

省略生命周期标记

在某些情况下,Rust允许我们在写函数的时候省略掉显式生命周期标记。

在这种时候,编译器会通过一定的固定规则为参数和返回值指定合适的生命周期,从而省略一些显而易见的生命周期标记。比如我们可以写这样的代码:

Rust-借用和生命周期,Rust,rust,开发语言,后端
实际上,它等同于下面这样的代码,只是把显式的生命周期标记省略掉了而已:

Rust-借用和生命周期,Rust,rust,开发语言,后端文章来源地址https://www.toymoban.com/news/detail-804156.html

到了这里,关于Rust-借用和生命周期的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Rust语法:所有权&引用&生命周期

    垃圾回收管理内存 Python,Java这类语言在管理内存时引用了一种叫做垃圾回收的技术,这种技术会为每个变量设置一个引用计数器(reference counter),来统计每个对象的引用次数。 一旦某个对象的引用数为0,垃圾回收器就会择取一个时机将其所占用的空间回收。 以Python为例子

    2024年02月12日
    浏览(54)
  • Rust之泛型、trait与生命周期

    泛型是具体类型或其他属性的抽象替代。在编写代码时,可以直接描述泛型的行为,或者它与其他泛型产生的联系,而无须知晓它在编译和运行代码时采用的具体类型。 们可以在声明函数签名或结构体等元素时使用泛型,并在随后搭配不同的具体类型来使用这些元素。 当使

    2024年02月13日
    浏览(34)
  • rust踩雷笔记3——生命周期的理解

    生命周期是rust中最难的概念——鲁迅 这一块内容即便是看rust圣经,第一遍也有点懵。今天早上二刷突然有了更直观的认识,记录一下。 概念和基本使用 生命周期就是应用的有效作用域,它的主要作用是避免悬垂引用。 悬垂引用的典型例子: 简而言之就是y引用的变量在y被

    2024年02月12日
    浏览(28)
  • 文盘Rust -- 生命周期问题引发的 static hashmap 锁 | 京东云技术团队

    2021年上半年,撸了个rust cli开发的框架,基本上把交互模式,子命令提示这些cli该有的常用功能做进去了。项目地址:https://github.com/jiashiwen/interactcli-rs。 春节以前看到axum已经0.4.x了,于是想看看能不能用rust做个服务端的框架。 春节后开始动手,在做的过程中会碰到各种有趣

    2024年02月09日
    浏览(36)
  • Rust-借用检查

    Rust语言的核心特点是:在没有放弃对内存的直接控制力的情况下,实现了内存安全。 所谓对内存的直接控制能力,前文已经有所展示:可以自行决定内存布局,包括在栈上分配内存,还是在堆上分配内存;支持指针类型;可以对一个变量实施取地址操作;有确定性的内存释

    2024年01月19日
    浏览(37)
  • rust reborrow - 重借用

    两个知识点: 第一:对于不可变借用T,它的传递属于Copy语意。对于可变借用mut T它的传递属于Move语意或reborrow。 第二:可变引用在同一个时刻只能拥有一个,但是有一个重借用(reborrow)的方式,可以让借用重新获得可变引用。 下面为reborrow的三种方式 明确写出接收变量的类

    2024年02月15日
    浏览(38)
  • Rust 中的引用与借用

    目录 1、引用与借用  1.1 可变引用 1.2 悬垂引用 1.3 引用的规则 2、slice 类型  2.1 字符串字面量其实就是一个slice 2.2 总结 在之前我们将String 类型的值返回给调用函数,这样会导致这个String会被移动到函数中,这样在原来的作用域不可访问了,但是我们功能一个String值得引用,

    2024年02月05日
    浏览(32)
  • Rust 基础入门 —— 2.3.所有权和借用

    Rust 的最主要光芒: 内存安全 。 实现方式: 所有权系统 。 因为我们这里实际讲述的内容是关于 内存安全的,所以我们最好先复习一下内存的知识。 然后我们,需要理解的就只有所有权概念,以及为了开发便利,进一步引出的引用借用概念。 内存作为存储程序运行时数据

    2024年02月12日
    浏览(35)
  • Rust语言从入门到入坑——(2)Rust在windows上搭建开发环境

    开始搭建一个适合在windows上运行的Rust环境。 Rust支持的程序语言很多:可详见官网介绍 本文章主要是在windowns下搭建开发环境 首先,需要安装最新版的 Rust 编译工具和 Visual Studio Code。 Rust 编译工具:https://www.rust-lang.org/zh-CN/tools/install Visual Studio Code:https://code.visualstudio.com

    2024年02月09日
    浏览(50)
  • Rust软件外包开发语言的特点

    Rust 是一种系统级编程语言,强调性能、安全性和并发性的编程语言,适用于广泛的应用领域,特别是那些需要高度可靠性和高性能的场景。下面和大家分享 Rust 语言的一些主要特点以及适用的场合,希望对大家有所帮助。北京木奇移动技术有限公司,专业的软件外包开发公

    2024年02月12日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包