RUST 每日一省:生命周期作用域

news/2024/7/7 20:44:15

生命周期

        一个变量的生命周期就是它从创建到销毁的整个过程。

作用域
        我们声明的每个变量都有作用域。作用域其实是变量和值存在的环境。作用域是由一对花括号表示的。例如,使用块表达式会创建一个作用域,即任何以花括号开头和结尾的表达式。此外,作用域支持互相嵌套,并且可以在子作用域中访问父作用域的元素,但反过来不行。

{
    let a = "hello";    ----------+-- a的作用域
    let b = 1;          -----+--b |
    {                        |    |
    let c = true;   ---+--c  |    |
                       |     |    |
                    ---+     |    |
    }                        |    |
                    ---------+    |
                    --------------+
}

        当作用域结束时,作用域内定义的变量都会运行相关代码以释放资源。对于在堆栈上分配的数据,可以轻松地判定变量是否存续。对在堆上分配的值, drop 方法会被放在作用域结束标记}之前调用。但这里是隐式的,可以避免程序员忘记释放值。 drop 方法来自 Drop 特征,它是为 Rust 中大部分堆分配类型实现的,可以轻松地自动释放资源。

生命周期&作用域

        Rust的生命周期是基于作用域的,我们可以认为变量的生命周期就是其作用域; 编译器能自动识别作用域内这些变量的生命周期,方便进行管理。其实生命周期纯粹是一个编译期构造,它可以帮助编译器确定某个引用有效的作用域,并确保它遵循借用规则。它可以跟踪诸如引用的来源,以及它们是否比借用值生命周期更长这类事情。Rust 中的生命周期能够确保引用的存续时间不超过它指向的值。生命周期并不是作为开发人员要用到的,而是编译器使用和推断引用的有效性时会用到的。

{
    let a = "hello";    ----------+-- a生命周期
    let b = 1;          -----+--b |
    {                        |    |
    let c = true;   ---+--c  |    |
                       |     |    |
                    ---+     |    |
    }                        |    |
                    ---------+    |
                    --------------+
}

所有权的转移 

        由于变量会在作用域结束时,会自动释放。如果我们需要在其作用域外继续使用它,要么转移所有权, 要么按位复制,就可以继续使用, 这取决于该变量是复制语义还是移动语义的。

        如果有其他变量进入了作用域,也是会发生所有权的变化;要么转移所有权, 要么按位复制, 这取决于该变量是复制语义还是移动语义的。

{
    let a = "hello".to_string();     
    let b = 1;   
    let c_out;  
    let d_out;        
    {                         
        let c_in = 2;  
        let d_in = "world".to_string();                    
        a;
        b;
        c_out = c_in;
        d_out = d_in;

    }     
    //println!("a:{}",a);     
    println!("b:{}",b);   
    println!("c_out:{}",c_out);
    //println!("c_in:{}",c_in);
    //println!("d_in:{}",d_in);   
    println!("d_out:{}",d_out);   
}

b:1
c_out:2
d_out:world

        如果我们要把打印a的注释去掉,就会产生如下错误,a进入子作用域之后,发生所有权转移,在自作用结束的时候,释放了a,所以我们继续使用就会报错了。

 去掉c_in的打印,则会产生如下错误,c_in也是如此。

创建新的作用域

  • 可以使用块表达式(花括号)创建作用域。
{
    let a = "hello".to_string();     
    let b = 1;   
    let c_out;  
    let d_out;        
    {                         
        let c_in = 2;  
        let d_in = "world".to_string();                    
        a;
        b;
        c_out = c_in;
        d_out = d_in;

    }     
    //println!("a:{}",a);     
    println!("b:{}",b);   
    println!("c_out:{}",c_out);
    //println!("c_in:{}",c_in);
    //println!("d_in:{}",d_in);   
    println!("d_out:{}",d_out);   
}
  • match匹配也会产生一个作用域。
  • for、 loop以及while循环语句均可以创建新的作用域。
  • if let块和while let块也会创建新的作用域。

        这三者都是类似,我们以match为例,演示一下。如果t换成是Some(6),就没有问题,因为t是Option<i32>,具有复制语义了。

fn main(){
    let t = Some("test".to_string());
    match t{
        Some(v) => (),
        None => (),
    }
    //println!("t:{}",t);
}

去掉注释之后,报错如下,说明t已经在Some(v)=> ()发生了所有权的转移,然后在作用域结束时被释放掉了;

  • 函数体本身是独立的作用域。

由于String是移动语义,当它作为参数传入f函数后,发生了所有权转移,如果在main函数中再次调用就会发生错误。

fn f(t:String){
   println!("{:?}",t);
}
fn main(){
    let t = Some("test".to_string());
    f(t);
    //println!("{:?}",t);
}

  • 闭包

闭包会创建新的作用域, 对于环境变量来说有以下三种捕获方式:
· 对于复制语义类型, 以不可变引用(&T) 来捕获。
· 对于移动语义类型, 执行移动语义(move) 转移所有权来捕获。
· 对于可变绑定, 如果在闭包中包含对其进行修改的操作, 则以可变引用(&mut) 来捕获。


fn main(){
    let t = Some("test".to_string());
    let c = |i: i32|{
     //println!("{:?},{:?}",t,i);
    t;
    };
    c(0);
    //println!("{:?}",t);
}

这里还有一个比较有意思的地方,就是println!("{:?},{:?}",t,i);如果在闭包里只有这一句;没有t; 则不会发生所有权的转移;即println不发生所有权的转移,他表面调用的时候t,实际的参数&t;


http://lihuaxi.xjx100.cn/news/1075823.html

相关文章

关于业务缓存的存储结构

问&#xff1a;为什么不直接缓存 [账号id->权限列表]的关系&#xff0c;而是 [账号id -> 角色id -> 权限列表]&#xff1f; 答&#xff1a;[账号id->权限列表]的缓存方式虽然更加直接粗暴&#xff0c;却有一个严重的问题&#xff1a; 通常我们系统的权限架构是RBA…

Reid之网络的定义代码详解

这篇文章是yolov5_reid的扩展。针对reid网络定义代码的详解&#xff0c;有助于大家的理解&#xff0c;同时也方便网络方面的改进。 数据集的预处理代码可以参考我另一篇文章&#xff1a;Reid数据集处理 本篇文章Reid的网络将以Resnet50为例。因此需要大家对Resnet代码有一定的…

Clickhouse分布式表引擎(Distributed)写入核心原理解析

Clickhouse分布式表引擎&#xff08;Distributed&#xff09;写入核心原理解析 Clickhouse分布式表引擎&#xff08;Distributed&#xff09;写入核心原理解析Clickhouse分布式表引擎&#xff08;Distributed&#xff09;查询核心原理解析 Distributed表引擎是分布式表的代名…

【接口自动化测试】月薪12k必会技术,从0到1学习接口自动化测试,6个操作安排的明明白白

导读&#xff1a;在所有的开发测试中&#xff0c;接口测试是必不可少的一项。有效且覆盖完整的接口测试&#xff0c;不仅能保障新功能的开发质量&#xff0c;还能让开发在修改功能逻辑的时候有回归的能力&#xff0c;同时也是能优雅地进行重构的前提。编写接口测试要遵守哪些原…

selenium定位网络元素

这里主要写的是新版本 1、通过元素的id属性来定位元素 ele driver.find_element(By.ID, “IamID”) # 类型为 WebElement 类 1 2、 通过元素的name属性来定位元素 driver.find_element(By.NAME, “first”).send_keys(“通过元素的name属性来定位元素”) 1 2 3、通过元素的…

01 【Sass的安装使用】

1.介绍 1.1 CSS预处理技术&#xff0c;及种类介绍 什么是css预处理技术 CSS 预处理器定义了一种新的语言&#xff0c;其基本思想是&#xff0c;用一种专门的编程语言&#xff0c;为 CSS 增加了一些编程的特性&#xff0c;将 CSS 作为目标生成文件&#xff0c;然后开发者就只…

Hilt 和协程助力启动框架搭建:解决代码混乱和初始化策略问题

关于Hilt的使用&#xff0c;目前已经比较普及了&#xff0c;想必大家已经知道。今天说的是一个如何利用Hilt来做一个启动框架的故事。 是否经历过大型项目的启动优化&#xff0c;一遍过去无任何效果&#xff0c;第二遍过去好几处报错&#xff0c;第三遍过去启动不了&#xff0…

C++:Article : 链接器(三):库与可执行文件的生成

链接器&#xff1a;库与可执行文件 1. 静态库1.1 静态链接下&#xff0c;可执行文件如何生成 2. 动态库2.1 动态库特点以及与静态库使用方式差异2.2 动态库和静态库使用时间 3. load-time dynamic linking&#xff08;加载时动态链接&#xff09;3.1&#xff1a;阶段一&#xf…