当前位置: 首页 > news >正文

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

生命周期

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

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

{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://www.lryc.cn/news/62406.html

相关文章:

  • 【过程8】——能量守恒视角总结感受
  • kong(4):限流配置
  • 人脸识别 Face Recognition 入门
  • 【AI绘画】Midjourney的使用及程序示例
  • 无公网IP?教你在外远程访问本地wamp服务器「内网穿透」
  • leetcode 628. 三个数的最大乘积
  • fork函数如何创建进程,exit/_exit函数如何使进程终止的详细分析与代码实现
  • 重置电脑时提示“缺少所需的驱动器分区”怎么办?
  • 在KylinV10安装Dm8
  • 「SQL面试题库」 No_46 交换工资
  • SLAM论文速递【SLAM—— RDS-SLAM:基于语义分割方法的实时动态SLAM—4.24(1)
  • OJ练习第82题——填充书架
  • OHOS IDE和SDK的安装方法
  • New Year Garland(计数类DP)
  • 32岁阿里P7,把简历改成不知名小公司,学历改成普通本科,工作内容不变,投简历全挂!...
  • 从三室心脏MRI影像检测主动脉瓣病变
  • 【JavaWeb】JavaScript
  • Apache Doris 1.2.4 Release 版本正式发布|版本通告
  • 【C++STL】map
  • vue2项目PC端如何适配不同分辨率屏幕
  • CorelDRAW2023最新版本图像设计软件
  • 第64章 树型结构数据的前端渲染渲染显示示例
  • 超级国际象棋:第二个里程碑已完成
  • vue3 HTML 和静态资源
  • 5G基站外市电改造建设方案 (ppt可编辑)
  • C++ 类和对象(上)
  • 【BIM+GIS】BIM模型导入GIS软件之前的一些处理设置
  • js FileReader的常用使用方法
  • 网络威胁情报:数据的力量
  • shell:清理指定目录中指定天数之前的旧文件