Rust所有权,生命周期,借用-进阶

1. 变量定义

RUST在定义一个变量时,实际上把变量在逻辑上分成了两个部分:

  1. 变量内存块
  2. 变量内容
rust 复制代码
let mxsm :String = String::from("mxsm");
  • 变量声明语句定义了一个内存块,上面变量**mxsm**的声明定义一个内存块。
  • 变量类型定义了内存块内容的格式,上面代码String表示了这块内存块的内容格式是String类型
  • 变量初始化赋值则在内存块中写入初始化变量内容。上面就是将 mxsm 初始化内容写入对应的内存块中。

2. 所有权

说道所有权官方的给出的解释是:

  • 所有权是一组规则,是一组规则约束,那么这组规则用来干什么呢?就是下面说的第二点
  • 控制程序如何管理内存

所有权的设计是为了保证对变量进行正确的清理操作。

2.1. 所有权规则

所有权有如下三条规则:

  • Rust中的每一个值都有一个拥有者
  • 同一时间值只能有一个拥有者
  • 当拥有者超出范围,值将会被删除

总结一下就是:所有权指变量内容的独占性。 如何理解这个独占性?我们通过下面的代码进行说明

rust 复制代码
    fn main() {
        let mxsm: String = String::from("mxsm rust"); //此时mxsm拥有内存块内容"mxsm rust"
        let mxsm2 = mxsm; //此时mxsm1拥有内存块内容"mxsm rust"
        println!("{}", mxsm); //编译报错,mxsm变量失去了拥有内存块的内容"mxsm rust"
    }

内容的独占性也就是所有权规则里面说的:同一时间值只能有一个拥有者

上面代码中还涉及到了rust的move语义

2.2 move语义

所有权转移指的是变量内容在不同的内存块之间的转移(浅拷贝)。当变量内容转移到新的内存块,旧的内存块就失去了这个变量内容的所有权。由此可见,变量名实际仅代表一个内存块,内存块的变量内容与变量名是一个暂时关联关系,RUST定义这种关联关系为绑定

备注:实现Copy trait的类型变量不做所有权转移操作,实现Copy trait的类型可通过栈拷贝完成变量内容赋值,清理也可以仅通过通常的调用栈返回完成。 例如基础类型i32, 结构体Duration等等

2.3 生命周期

Rust被设计成自动调用变量类型的drop以完成清理,对变量的生命周期跟踪成为一个必然的选择,在判断变量的生命周期终结的时候调用变量的drop函数。

  • Rust采用生命周期仅与变量名相关联的设计

    rust 复制代码
        struct RefTest<'t> {
            pub a: &'t i32,
        }
  • Rust借用的生命周期应该短于所有权的生命周期

因为生命周期仅对变量有意义,而在转移所有权的操作中,是两个不同的变量发生的联系,他们的生命周期彼此独立。所以所有权转移时,所有权变量的类型层次上不涉及生命周期类型转换。如果类型成员中有引用,则见下面的内容。 当对一个引用类型变量做赋值时,便出现了生命周期类型转换,举例分析如下:

rust 复制代码
        fn main() {
            let b:i32 = 5;             // ----------+-- 'b
                                        //           |
            let a:&i32 = &b;           //  --+-- 'a  |
                                        //   |       |
            println!("b: {}", b);       //   |       |
                                        // --+       |
        }                               // ----------+
  1. 当声明一个类型引用的变量时,例如:let a: &i32 实质声明了一个i32类型引用的内存块,这个内存块有一个生命周期泛型, 假设为'a,
  2. 假设要对此变量赋值为另一个变量的引用,例如: let b:i32 = 4; a = &b; &b实质是对b的内存块进行引用,该内存块的生命周期假设为'b,
  3. 赋值实质是将一个&'b i32 类型的变量赋值给&'a i32类型变量。则必然发生类型转换 关系,这时,只有当'b是'a的子类型时,即'b长于'a时,这个类型转换才能被编译器认为正确。

以上实际就是生命周期的奥秘所在了,Rust对生命周期设计的关键点就是:

  1. 在变量赋值时捕捉触发生命周期类型转换的情况
  2. 确保类型转换不正确时,给出生命周期不正确的编译错误警告。

对生命周期推断的复杂性,Rust采用了每个函数自决的方式(推断)。 每一个函数的生命周期类型转换处理正确与否在函数内完成判断(以下为根据逻辑进行的推断,可能不准确):

  1. 函数作用域会有一个生命周期泛型;
  2. 函数的定义会定义函数参数的生命周期泛型,以及这些生命周期泛型之间的继承关系。显然,函数作用域生命周期泛型是所有输入参数生命周期泛型的基类型
  3. 函数的定义会定义输出的生命周期泛型,以及输出的生命周期泛型与输入参数生命周期泛型的继承关系。如果输出是一个借用或由借用派生的类型或者有借用成员的复合类型,则输出的生命周期泛型必须是某一输入生命周期泛型的基类型。
  4. 编译器会分析函数中的作用域,针对每个作用域生成生命周期泛型,并形成这些生命周期泛型之间的继承关系,当然,函数内所有生命周期泛型都是函数作用域生命周期泛型的基类型。
  5. 根据这些生命周期泛型及他们之间的继承关系,处理函数内操作时引发的生命周期泛型类型转换,并对错误的转换做出错警告。
  6. 如果调用了其他函数,则对调用函数的输入参数及输出之间的转换是否正确判断转移至调用函数。
相关推荐
Adolf_19931 小时前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
叫我:松哥1 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap
海里真的有鱼1 小时前
Spring Boot 项目中整合 RabbitMQ,使用死信队列(Dead Letter Exchange, DLX)实现延迟队列功能
开发语言·后端·rabbitmq
工业甲酰苯胺2 小时前
Spring Boot 整合 MyBatis 的详细步骤(两种方式)
spring boot·后端·mybatis
新知图书2 小时前
Rust编程的作用域与所有权
开发语言·后端·rust
wn5313 小时前
【Go - 类型断言】
服务器·开发语言·后端·golang
希冀1233 小时前
【操作系统】1.2操作系统的发展与分类
后端
GoppViper4 小时前
golang学习笔记29——golang 中如何将 GitHub 最新提交的版本设置为 v1.0.0
笔记·git·后端·学习·golang·github·源代码管理
爱上语文5 小时前
Springboot的三层架构
java·开发语言·spring boot·后端·spring
serve the people5 小时前
springboot 单独新建一个文件实时写数据,当文件大于100M时按照日期时间做文件名进行归档
java·spring boot·后端