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 mutablelet 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的函数及模块系统。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

shell 脚本的函数和数组

函数 —— 封装的一个公式&#xff1a;sin、cos、tan —— 函数为脚本的别名 —— 函数就是一个功能模块&#xff0c;在函数中写执行的命令即可&#xff1b;使用函数可以避免代码重复&#xff0c;增加可读性&#xff0c;简化脚本&#xff0c;使用函数可以将大的工程分割为若…

Vue中动态class方法

1.方法一&#xff1a;只需要展示一个class样式&#xff0c;但是动态取值 <div :class"aaa"></div>data:{return () {aaa:colorRed} }.colorRed {color: red; } 2.方法二&#xff1a;表达式判断是否展示class里面的样式 <div :class"{colorRed…

函数与数组

一.函数 1、函数的作用 定义较为复杂的但是需要重复使用的内容&#xff0c;以便再次使用&#xff0c;可以直接调用&#xff0c;节约时间&#xff0c;提高效率。 语句块定义成函数约等于别名&#xff0c;定义函数&#xff0c;再引用函数。 封装的可重复利用的具有特定功能的…

python+django高校科研项目管理系统2u3mx

高校科研项目管理系统采用拟开发的高校科研项目管理系统通过测试,确保在最大负载的情况下稳定运转,各个模块工作正常,具有较高的可用性。系统整体界面简洁美观,用户使用简单,满足用户需要。在因特网发展迅猛的当今社会,高校科研项目管理系统必然会成为在数字信息化建设的一个重…

Linux 查看 CPU 信息相关命令

目录 lscpu top htop lscpu lscpu 命令是用于显示有关 CPU 架构的信息&#xff0c;而不是用于实时监视 CPU 负载。它提供有关 CPU 类型、核心数、线程数、架构等的详细信息。 lscpu top 命令会显示系统的实时状态&#xff0c;包括 CPU 使用率、内存使用率等。按下键盘上的…

一文详解Vue生命周期

Vue是一种流行的用于构建用户界面的渐进式JavaScript框架。Vue框架在开发过程中&#xff0c;特别强调对生命周期的理解和管理。通过使用生命周期钩子函数&#xff0c;开发者能够精确地控制Vue实例的创建、挂载、更新以及销毁过程。本文将对Vue的生命周期进行详细的介绍&#xf…

YOLOv8训练自己的目标检测数据集

YOLOv8训练自己的目标检测数据集 目录标题 源码下载环境配置安装包训练自己的数据集数据集文件格式数据集文件配置超参数文件配置训练数据集命令行训练脚本.py文件训练 进行detect显示detect的效果 源码下载 YOLOv8官方的GitHub代码&#xff0c;同时上面也有基础环境的配置要…

IntelliJ IDEA 16创建Web项目

首先要理解一个概念&#xff1a;在IntelliJ IDEA中“new Project”相当于eclipse中的工作空间&#xff08;Workspace&#xff09;&#xff0c;而“new Module”相当于eclipse中的工程&#xff08;Project&#xff09;。以下均采用Intellij的说法&#xff0c;请自行对照转换理解…

Maven仓库清除remote.repositories、lastUpdated、sha1脚本

Maven仓库清除remote.repositories、lastUpdated、sha1脚本 1. 脚本功能 清除本地maven仓库的_remote.repositories、sha1、lastUpdated的文件 2. 脚本内容 set REPOSITORY_PATH%cd% rem 搜索中... for /f "delims" %%i in (dir /b /s "%REPOSITORY_PATH%\*…

Java 关键字:synchronized详解

synchronized详解 基本使用源码解析常见面试题好书推荐 基本使用 Java中的synchronized关键字用于在多线程环境下确保数据同步。它可以用来修饰方法和代码块 当一个线程访问一个对象的synchronized方法或代码块时&#xff0c;其他线程将无法访问该对象的其他synchronized方法或…

【愚公系列】2023年11月 Java教学课程 188-SpringCloud(Feign远程调用)

&#x1f3c6; 作者简介&#xff0c;愚公搬代码 &#x1f3c6;《头衔》&#xff1a;华为云特约编辑&#xff0c;华为云云享专家&#xff0c;华为开发者专家&#xff0c;华为产品云测专家&#xff0c;CSDN博客专家&#xff0c;阿里云专家博主&#xff0c;阿里云签约作者&#xf…

如何通过宝塔面板搭建一个本地MySQL数据库服务并实现远程访问

宝塔安装MySQL数据库&#xff0c;并内网穿透实现公网远程访问 文章目录 宝塔安装MySQL数据库&#xff0c;并内网穿透实现公网远程访问前言1.Mysql服务安装2.创建数据库3.安装cpolar3.2 创建HTTP隧道 4.远程连接5.固定TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网…

浙江师范黄昌勤老师关于情感分析的讲座

回放&#xff1a;https://share.weiyun.com/Wvaw4OoY 收获很大&#xff1a; 写几点&#xff0c; 1&#xff1a;小二聚焦 精细更有用 2&#xff1a;高产的秘诀就是 专注一个领域&#xff0c;不断延展。 3&#xff1a;好的文章技术不是本质&#xff0c;本质还是解决了什么教育问…

九、ffmpeg命令转封装

开了几天小差&#xff0c;今天继续学习ffmpeg。 准备测试使用的视频&#xff0c;并查看其信息 # 查看视频信息。使用Mediainfo也可以 ffprobe test.mp4 视频格式的信息如下。 保持编码格式&#xff1a;ffmpeg -i test.mp4 -vcodec copy -acodec copy test_copy.tsffmpeg -i…

WordPress网站如何修复数千个帖子的SEO错误

在本教程中&#xff0c;我们将向您展示如何解决您经常犯的SEO错误。 最好的是您不必花费太多时间&#xff0c;因为您不需要打开并编辑每个帖子。 相反&#xff0c;我们将向您展示如何使用 WordPress 内的电子表格来修复 WordPress 帖子的 SEO。 在这里&#xff0c;我们为您提…

nint和Pattern matching介绍(C#)

nint 最近看C# 9.0时&#xff0c;发现一个有意思的关键词&#xff0c;就是nint&#xff0c;第一次看到这个&#xff0c;于是好奇心爆棚&#xff0c;就去实际操作了一下。 nint i 1000; Console.WriteLine("i{0}", i);实际结果与int的结果是一样的&#xff0c;那为什…

ARM裸机-19(NandFlash和iNand)

1、NandFlash的接口 1.1、Nand的型号与命名 (1)、Nand的型号命名都有含义&#xff0c;就拿K9F2G08来示例分析一下&#xff1a;K9F表示是三星公司的NandFlash系列。2G表示Nand的大小是2Gbit (256MB)。08表示Nand是8位的 (8位就是数据线有8根)。 (2)、Nand命名中可以看出&#x…

【2023.11.23】JDBC基本连接语法学习➹

1.导入jar包依赖&#xff1a;mysql-connector-java-8.0.27.jar 2.连接数据库&#xff01; 3.无法解析类->导入java.sql.*&#xff0c;&#xff08;将项目方言改为Mysql&#xff09; JDBC&#xff0c;启动&#xff01;&#xff01; public class Main {public static voi…

如何下载OpenJDK及其源码

如果想下载 OpenJDK&#xff0c;存在以下几种办法&#xff1a; 最简单的办法是去 OpenJDK 官网&#xff0c;这里能下载 JDK9 及其以上的版本&#xff0c;还有 JDK 源码所在的 github 地址。 第二种方法是使用 IDEA 下载&#xff0c;位置在 File->Project Structure->SD…

【Vue】核心特性(响应式)

响应式&#xff1a; 数据变化&#xff0c;视图自动更新 接下来使用一个例子来体现一下什么是响应式 案例一&#xff1a; 访问数据&#xff0c;视图自动更新 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><…