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. 如果调用了其他函数,则对调用函数的输入参数及输出之间的转换是否正确判断转移至调用函数。
相关推荐
.生产的驴43 分钟前
SpringBoot 封装统一API返回格式对象 标准化开发 请求封装 统一格式处理
java·数据库·spring boot·后端·spring·eclipse·maven
景天科技苑1 小时前
【Rust】Rust中的枚举与模式匹配,原理解析与应用实战
开发语言·后端·rust·match·enum·枚举与模式匹配·rust枚举与模式匹配
红尘散仙1 小时前
七、WebGPU 基础入门——Texture 纹理
前端·rust·gpu
红尘散仙1 小时前
八、WebGPU 基础入门——加载图像纹理
前端·rust·gpu
w4ngzhen2 小时前
关于Bevy中的原型Archetypes
rust·游戏开发
追逐时光者2 小时前
MongoDB从入门到实战之Docker快速安装MongoDB
后端·mongodb
方圆想当图灵2 小时前
深入理解 AOP:使用 AspectJ 实现对 Maven 依赖中 Jar 包类的织入
后端·maven
豌豆花下猫2 小时前
Python 潮流周刊#99:如何在生产环境中运行 Python?(摘要)
后端·python·ai
嘻嘻嘻嘻嘻嘻ys2 小时前
《Spring Boot 3 + Java 17:响应式云原生架构深度实践与范式革新》
前端·后端
异常君2 小时前
线程池隐患解析:为何阿里巴巴拒绝 Executors
java·后端·代码规范