Rust语言入门教程(二) - 变量与作用域

变量与作用域

变量的声明与初始化

Rust的基本语法格式如下:

fn main(){
	let bunnies = 2;
}

语句以分号结尾,用花括号包含语句块。 Rust的语法其实借鉴了很多其他的语言,比如C语言和Python, 所以变量定义的格式看起来也跟很多我们熟悉的其他语言相似。Rust中,使用let关键字声明一个变量。在上面的例子中, 我们声明了一个变量bunnies, 并且初始化了它的值为2.

Rust是一种强类型的语言,那么在上面的语句中,哪里标注了这个变量的类型呢?在Rust编程中,如果Rust能准确的识别这个变量的类型,那么我们不需要显式的标注变量的类型,也不需要像C#那样标注一个auto表示它的类型是自动识别的。

如果需要显式的标注一个变量的类型,可以像下面的例子一样做, 在变量名后加个: , 后面再写上变量的类型,如下,i32代表有符号的32位整型。

fn main(){
	let bunnies: i32 = 2;
}

与python类似,rust也可以在一行语句中定义多个变量。如下例子便可以在一行代码中为两个变量初始化:

fn main(){
	let (bunnies, carrots) = (8, 50);
}

变量不可变

在Rust中,变量默认其实是不可变的,也就是说,一旦对一个变量赋值以后,其值默认是不可被修改的。这一特点与大多数的其他编程语言都不同,其他编程语言的变量默认是随时可以被重新赋值的。那为什么Rust要将变量设置为默认不可变的呢?这就要提到上一章中我们提到的Rust的三个特性了:

  • 内存安全: 如果变量在运行过程中始终不变,这可以避免很多bug的发生,变量不可变这一设计,极大的提高了Rust的内存安全特性。
  • 无畏并发: 不变的变量,可以被多个线程在不加锁的情况下共享,这也使得Rust的并发更安全可靠。
  • 高性能: 不变的变量,使得编译器可以对其进行额外的优化,从而提高了代码的执行速度,提高了程序运行性能。

但是不得不承认我们在编程中一定会遇到需要修改变量的需求, 如果我们直接修改变量的值,编译便会报错,例如下面的代码:

fn main(){
	let bunnies: i32 = 2;
	bunnies = 3;   // Error!
}

如果运行上面的代码,将会得到下面的报错,可以看到,报错中非常明确的指出了代码的问题所在,并且还指出了修改建议, 在报错的最上面,给出了错误的描述,也就是:不能对不可变变量进行二次赋值。在报错中,也指出了错误所在的位置,第3行第5列。接下来还对整个错误的上下文进行了说明,告诉我们在第2行的时候对变量bunnies已经赋值,然后再第3行再次对不可变变量bunnies进行了赋值,因此报错。接着还提出了修改建议,让我们在第2行的变量名前面加上mut, 使其成为一个可变变量,也许能修复这个问题。在最后一行,如果上面的提示还不能解决问题,还可以运行rustc --explain e0384来查看错误的完整描述。
请添加图片描述
按照错误提示,我们将代码修改后如下便可以成功运行了:

fn main(){
	let mut bunnies: i32 = 2;
	bunnies = 3;   // Error!
}

常量

在Rust中,常量(constant)其实也属于变量的一种, 相比普通的不可变变量,它更加的不可变。定义一个常量包含以下四个关键步骤:

  • const而不是let声明;
  • 变量名格式为全大写字母加下划线分隔;
  • 必须声明变量类型;
  • 常量的值必须时编译时可确定值的表达式;

下面是普通变量和常量声明的对比:

let wrap_factor = ask_scotty();  // 变量
const WRAP_FACTOR: f64 = 9.9;    // 常量

定义一个常量比变量麻烦很多,那为什么还要用常量呢?

  • 常量可以在函数作用域外或者模块外进行定义,而在任意的地方使用;
  • 常量会在编译时被静态的写入可执行文件,使得运行速度很快;
  • Rust官方在每个发布版本中都对const类型增加了越来越多的功能和优化,在可以使用const的地方,使用const是一个好的选择;

作用域

每个变量都有各自的作用域,只有在变量的作用域中,变量才能被使用。代码的作用域通常是从变量被创建的地方开始,到变量所在的代码块结束, 在这个范围中的子代码块中,变量仍然是可以被访问的。

注: 代码块是一组被花括号包含的语句

fn main() {
    let x = 5;
    {
        let y = 99;
        println!("x = {}, y = {}", x, y);
    }
    println!("x = {}, y = {}", x, y); // Error!
}

在上面的代码中, 变量xmain函数的代码块中被定义,其中定义了一个子代码块,在子代码块中定义了一个变量y, 在子代码块中,xy都可以被访问, 在子代码块结束时, y立刻被销毁(Rust中没有任何的垃圾回收器,变量总是在离开作用域后被立即销毁),因此第二个println!语句不能访问变量y而发生错误。

然而我们不用担心这会在运行时发生bug, 因为这种错误会在编译时就被暴露出来。
请添加图片描述

变量隐藏

Rust中,也存在变量隐藏的现象

fn main() {
    let x = 5;
    {
        let x = 99;
        println!("x = {}", x);
    }
    println!("x = {}", x); // Error!
}

运行结果应该如下:

x = 99
x = 5

在上述代码中我们在子代码块外部定义了一个变量x并赋值为5, 在子代码块中,x的值被覆盖,为内层代码块中的值99。当离开了内层代码块后,内层的变量x被销毁, x的值又变回了外层代码块中的5.

再来看一个例子:

fn main() {
	let mut x = 5; // x is mutable
	let x = x;     // x is now immutable
}

这个例子中,第一个x被隐藏了,这其实相当于重新声明并初始化了x这个变量,在编译过程中, Rust甚至能识别到这种情形并优化执行的过程,并不会真的先定义一个可变的x, 再用一个新的x去覆盖它,而是直接定义一个不可变的变量x并为其赋值为5.

再看一个例子:

fn main() {
    let meme = "More cowbell!";
    let meme = make_image(meme);
}

在上述代码中, 变量meme甚至能被改变类型(从字符串变成了图片)。

变量与内存安全

在Rust中,在使用一个变量前,必须确保这个变量被初始化。

情景A

fn main() {
    let enigma: i32;
    println!("{}", enigma);  // Error!
}

请添加图片描述
可以看到,报错提示我们,变量虽然被声明了,但是没有被初始化。

情景B

fn main() {
    let enigma: i32;
    if true{
		enigma = 42;
	}
    println!("{}", enigma);  // Error!
}

请添加图片描述
即时是在一个恒为真的判断语句中为变量进行了初始化,编译器仍会报错, 因为判断语句只有在运行时才能被判别最终的结果,因此在编译时没办法确保该变量一定会被初始化。

为了保证变量一定被初始化,可以将上述代码改为如下:

fn main() {
    let enigma: i32;
    if true{
		enigma = 42;
	} else {
		enigma = 7;
	}
    println!("{}", enigma);  // Error!
}

如果在C语言中使用了一个未初始化的变量会出现什么现象呢,如下代码:

include <stdio.h>
int main(){
	int enigma;
	printf("%d\n", enigma);
}

这将不会导致编译报错,程序可以正常运行,但是会输出一个不可预测的结果,因为声明变量后, C语言就会在内存分配一个地址,而这个内存地址中存储的是什么数据,我们不得而知,它可能是任何东西。

小结

本章介绍了Rust中变量的种类,声明与赋值方式,以及变量的作用域和隐藏特性。下一章将介绍Rust的函数及模块系统。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/181200.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Windows如何使用key登录Linux服务器

场景&#xff1a;因为需要回收root管理员权限&#xff0c;禁止root用户远程登录&#xff0c;办公环境只允许普通用户远程登录&#xff0c;且不允许使用密码登录。 一、生成与配置ssh-key 1.使用root管理员权限登录到目标系统。 2.创建一个新的普通用户&#xff0c;和设置密码用…

java--static的注意事项

1.使用类方法、实例方法时的几点注意事项 ①类方法中可以直接访问类的成员&#xff0c;不可以直接访问实例成员。 ②实例方法中既可以直接访问类成员&#xff0c;也可以直接访问实例成员。 ③实例方法中可以出现this关键字&#xff0c;类方法中不可以出现this关键字的。

教育机构拒绝“数据陷阱”,群硕将英孚新一代教学管理系统搬上桌

为什么小机构年年担心招生不够&#xff0c;英孚却令学生家长趋之若鹜&#xff1f; 区别就在教学管理方式。为了更好地管理分布全球的校区、学生和老师&#xff0c;英孚应用了一套教学管理系统&#xff0c;帮助学校管理学员&#xff0c;帮老师智慧排课&#xff0c;帮助家长记录…

地埋式积水监测仪厂家直销推荐,致力于积水监测

地埋式积水监测仪是一种高科技设备&#xff0c;能够实时监测地面积水深度&#xff0c;并及时发出预警信息&#xff0c;有效避免因积水而产生的安全隐患。这种智能监测仪可以安装在城市道路、立交桥、地下车库等易积水地势较低的地方&#xff0c;以确保及时监测特殊地段的积水&a…

边海防可视化智能视频监控与AI监管方案,助力边海防线建设

一、背景与需求 我国有3万多公里的边境线和海岸线&#xff0c;随着我国边海防基础设施建设的快速发展&#xff0c;边海安防也逐渐走向智能化。传统人工巡防的方式已经无法满足边海智能化监管的需求&#xff0c;在沿海、沿边地区进行边海智慧安防视频监控系统等边海防基础设施建…

TP5制作图片压缩包

目标:将多张图片制成在一个压缩包内,供调取使用 public function test() {//引入压缩包类$zip new \ZipArchive();//新定义一个zip包$zipname ROOT_PATH./public/zip/.date("YmdHis").rand(111,999)..zip;if ($zip->open($zipname, \ZipArchive::CREATE) true…

mongo DB -- aggregate分组查询后字段展示

一、分组查询 在mongoDB中可以使用aggregate中的$group操作对集合中的文档进行分组,但是查询后的数据不显示其他字段,只显示分组字段 aggregate进行分组示例 db.collection.aggregate([{$group: {_id: "$field"}},]) 查询后显示 展开只显示两个字段 二、显示所有字段…

linux通过串口传输文件

简介 在嵌入式调试过程中&#xff0c;我们经常会使用调试串口来查看Log或者执行指令&#xff0c;其实&#xff0c;调试串口还有另一种功能&#xff0c;就是传输文件&#xff0c;本文说明使用MobaXterm串口工具来传输文件。 环境要求 嵌入式系统需要安装lsz和lrz&#xff0c;…

【深度学习实验】图像处理(一):Python Imaging Library(PIL)库:图像读取、写入、复制、粘贴、几何变换、图像增强、图像滤波

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 安装 PIL 库1. 图像读取和写入a. 图像读取b. 图像写入c. 构建新图像 2. 图像复制粘贴a. 图像复制b. 图像局部复制c. 图像粘贴 3. 几何变换a. 图像调整大小b. 图像旋转c. 图像翻转 4. 图像增强a.…

智慧法院 | RPA+AI打造智慧执行助手,解决“案多人少”现实难题

为深化政法智能化建设&#xff0c;加强“智慧治理”“智慧法院”“智慧检务”“智慧警务”“智慧司法”等信息平台建设&#xff0c;深入实施大数据战略&#xff0c;实现科技创新成果同政法工作深度融合。法制日报社于今年3月继续举办了2023政法智能化建设创新案例及论文征集宣传…

在游戏开发中,实时渲染和离线渲染对于游戏平衡的影响有哪些?

实时渲染和离线渲染对游戏平衡有那些影响呢&#xff1f;在游戏开发中&#xff0c;渲染方式的选择对游戏的整体表现和玩家体验有着至关重要的作用。那么&#xff0c;实时渲染和离线渲染究竟有哪些利弊呢&#xff1f; 一、实时渲染 实时渲染&#xff0c;顾名思义&#xff0c;是…

新的预测模型的局部评价指标-pAUROCc

新的预测模型的局部评价指标-pAUROCc Background 局部评价主要是用在不平衡数据上&#xff0c;其合理性&#xff1a;1.局部评价比全局评价敏感&#xff0c;更容易区分模型的优劣&#xff1b;2.临床决策曲线&#xff08;DCA&#xff09;可知&#xff0c;模型使用过程中&#x…

【Spring集成MyBatis】MyBatis的Dao层实现(基于配置,非注解开发)

文章目录 1. MyBatis的dao层实现(传统方式)——需要写接口及其实现类2. MyBatis的代理开发方式——仅需写接口 1. MyBatis的dao层实现(传统方式)——需要写接口及其实现类 传统方式就是在项目下边建立dao包&#xff0c;里面包含接口及其实现类&#xff0c;文件结构如下&#x…

3D人脸扫描设备助力企业家数字人复刻,打破商业边界

京都薇薇推出数字人VN&#xff0c;以京都薇薇董事长为原型制作&#xff0c;赋能品牌直播、短片宣传、线上面诊等活动&#xff0c;进一步增强消费者对品牌的交互体验&#xff0c;把元宇宙与品牌相融合&#xff0c;推动品牌线上服务与线下服务实现数字一体化&#xff0c;打造一个…

Linux宝塔面板搭建Discuz论坛, 并内网穿透实现公网访问

Linux宝塔面板搭建Discuz论坛&#xff0c; 并内网穿透实现公网访问 文章目录 Linux宝塔面板搭建Discuz论坛&#xff0c; 并内网穿透实现公网访问前言1.安装基础环境2.一键部署Discuz3.安装cpolar工具4.配置域名访问Discuz5.固定域名公网地址6.配置Discuz论坛 &#x1f4f7; 江池…

基于C#实现线段树

一、线段树 线段树又称"区间树”&#xff0c;在每个节点上保存一个区间&#xff0c;当然区间的划分采用折半的思想&#xff0c;叶子节点只保存一个值&#xff0c;也叫单元节点&#xff0c;所以最终的构造就是一个平衡的二叉树&#xff0c;拥有 CURD 的 O(lgN)的时间。 从…

XC3320 离线式、无电感交流输入线性稳压器 可替代KP3310 固定5V输出电压

XC3320 是一款紧凑型无电感设计的离线式线性稳压器。XC3320 可获得 5V输出电压。XC3320 是一种简单可靠的获得偏置供电的离线式电源解决方案。XC3320 集成了 650V 功率 MOSFET&#xff0c;启动控制电路,VDD 电压控制电路,AC 交流信号同步检测电路&#xff0c;低压差稳压器等。该…

电动机保护方式

3.3.1、电动机温度保护 温度保护是利用安装在电动机内部的温度继电器或变换器来实现的。当电动机达到一定温度时继电器动作&#xff0c;通过控制电路断开电动机的主电路。对于单相小容量电动机&#xff0c;可以用继电器直接断开动力电路。 根据温度传感器的不同可以分为&…

项目管理中的资源日历是什么?有什么作用

管理项目不仅需要规划和预算&#xff0c;还需要日程安排。 资源日历是一种显示项目经理或团队领导在特定时间内可用资源的工具。这种类型的项目日历可以显示团队成员和设备在特定时间段内的可用工作时间。 例如&#xff0c;如果一名员工每天工作 8 小时&#xff0c;而他已经在…

软件开发及交付的项目管理角色

在软件开发及交付过程中&#xff0c;通常会涉及不同的角色和职责&#xff0c;包括业务角色、技术角色和管理角色。这些角色在项目管理中发挥着不同的作用&#xff0c;以确保项目的成功和交付高质量的产品。 业务角色&#xff1a;包括产品经理、业务分析师和业务运营人员等职位…