学Rust,进华为!学习Rust语言变量中的可变与不变性

查理谈科技 2024-05-07 15:26:55

学Rust ,进华为!

本文简单介绍Rust 语言中变量的可变性与不可变性,以及和常量的区别。

在编程语言中中,变量是指用于存储值的存储容器,是程序可以存储和检索数据的命名存储位置。 尽管变量的基本概念在各种编程语言中都是通用的,但是, Rust 对待变量的方式和其他语言略有不同。

下面让我详细来看一下Rust 语言在这方面的特点。

Rust 是一个强类型语言,变量与特定类型相关联。如果从“编程范式”、“动态静态类型”、“强弱类型”来区分不同类型的语言,那么Rust 语言应该属于哪一种呢?

编程语言的分类

先来看下表:

来源:https://cloud.tencent.com/developer/article/2327334

从编程范式来看, Rust 属于多范式语言,因为Rust支持可变变量和通过指令来改变程序状态,因而属于命令式语言,但是同时, Rust也支持如闭包和高阶函数等函数式编程的特性。

从动态和静态类型语言来看, Rust 毫无疑问是静态类型语言,代码中的变量必须在编译期间确定具体的类型。

从强弱类型语言来看, Rust是一种强类型语言,它有严格的类型系统,不允许不安全的类型操作,以确保内存安全和类型安全。

因而,Rust是一种“静态类型”的、强类型的、多范式编程语言,可以用于通用编程以及具有系统级性能和内存安全性要求的各种应用领域,尤其适用于安全关键应用的开发。

Rust 语言的不可变性

在Rust 语言中, 有一个有意思的设定,默认情况下,变量是不可变的。当变量是不可变的时,一旦将值绑定到名称,就无法更改该值。

为了说明这一点,下面我们来通过代码展现。使用 cargo new variable_mutable 在项目目录中生成一个新项目。

$ cargo new variable_mutable

然后,用VS Code 打开项目目录,打开 src/main.rs,添加如下代码:

fn main() { let x = 5; println!("变量x的值是: {}", x); x = 6; println!("变量x的值是:{}", x);}

在这个代码段中,我们首先用let定义一个变量x,给出的初始值是5; 然后再把这个值改为6.

从VS Code 中也可以看出,这段代码存在着语法问题:

把鼠标移动到第4行,可以看到这么一行提示“cannot mutate immutable variable x”, 就是说, 在代码编写期间的语法检查中, 已经看到了这个错误, 不能把一个不可变变量重新赋值。Rust 编译器保证,当你声明一个值不会改变时,它确实不会改变,所以你不必自己跟踪它。 因此,您的代码更容易推理。

那么,当我们想在后续的代码中改变这个变量, 那么该怎么办呢? Rust的设计者为你提供了mut 关键字, 在定义变量时, 如果在变量名称前面加上mut , 就可以声明一个可变变量:

let mut x = 5;

通过这种显式方式的方式, 我们一开始就能确定这个变量的值在后续会发生变化。

fn main() { let mut x = 5; println!("变量x的值是: {}", x); x = 6; println!("变量x的值是:{}", x);}

当我们在VS code 中点击Run 按钮时, 可以看到,这时候已经可以正常运行:

可以看出,当使用mut 关键字时, 就可以让变量发生变化。

Rust中的常量constant与不可变变量

在Rust 中, 变量如果没有前缀mut ,那么这个变量的值是不可改变的, 但是,Rust中仍然有常量(constant)的存在。常量是绑定到名称且不允许更改的值 ,那么, 常量与不可变变量有什么区别呢?

首先,从语法上,不允许将 mut 与常量一起使用。 常量不仅在默认情况下是不可变的,而且始终是不可变的。

在Rust中, 使用 const 关键字而不是 let 关键字声明常量,并且必须注释值的类型。

常量可以在任何范围内声明,包括全局范围,这使得它们对于代码的许多部分需要了解的值非常有用。

最后一个区别是常量只能设置为常量表达式,而不是只能在运行时计算的值的结果。

下面是常量声明的示例:

const FIVE_HOURS_IN_SECONDS: u32 = 60 * 60 * 5;

该常量的名称为 FIVE_HOURS_IN_SECONDS,其值设置为 60(一分钟的秒数)乘以 60(一小时的分钟数)乘以 3。

Rust 的常量命名约定是全部大写,单词之间使用下划线。 编译器能够在编译时会进行计算,因此在这里可以选择以更容易理解和验证的方式写出该值,而不是将此常量设置为值 10,800, 这是为了增加理解,避免“神秘数字”这种不良代码风格的出现。

常量在程序运行的整个时间内,在声明它们的范围内都有效。 此属性使常量对于程序的多个部分可能需要了解的应用程序域中的值非常有用,例如允许游戏玩家获得的最大分数或光速。

将整个程序中使用的硬编码值命名为常量有助于将该值的含义传达给未来的代码维护者。 如果将来需要更新硬编码值,那么在代码中只需要更改一个位置也很有帮助。

Rust中的变量覆盖(Shadow)

从C或者Java程序员的角度来说,在一个范围内,是不允许有同名的变量出现的。比如说我们在前面定义了一个变量a,在同一个范围内就不允许有另外一个变量a的定义。

但是,在Rust中, 这是可以的!

Rust可以声明一个与先前变量同名的新变量。 可以认为第一个变量被第二个变量遮蔽,这意味着,当这种情况发生时,编译器将看到第二个变量。 实际上,第二个变量覆盖第一个变量,将变量名的任何使用都归为自身,直到它本身被覆盖或作用域结束。

下面的代码表明,可以通过使用相同的变量名称并重复使用 let 关键字来隐藏变量,如下所示:

fn main() { //首次定义 let x = 5; println!("变量x的值是:{x}"); //第一次shadow let x = x + 1; println!("变量x的值是:{x}"); { //第二次shadow let x = x + 2; println!("变量x的值是:{x}"); { //第三次shadow let x = x * 2; println!("变量x的值是: {x}"); } println!("变量x的值是:{x}"); } //第四次shadow,回到原点 let x = x - 1; println!("变量x的值是:{x}");}

运行结果:

变量x的值是:5变量x的值是:6变量x的值是:8变量x的值是: 16变量x的值是:8变量x的值是:5

在这个代码中, 通过let 反复重新定义变量x, 每次变量x的值都会发生变化, 当退出相应的范围时, 变量x的值会回归到上一次变量的定义。

如果从C/Java等开发者的视角来看, Rust 的这个变量覆盖(Shadow)并不是很好的设计, 使得变量的定义前后不一致,实在不算是太好的设计!差评!

在Rust中,变量覆盖Shadow与将变量标记为 mut 不同,因为如果我们不小心尝试在不使用 let 关键字的情况下重新分配给该变量,我们将收到编译时错误。 通过使用let,我们可以对一个值执行一些转换,但在这些转换完成后变量是不可变的。

mut 和 Shadowing 之间的另一个区别是,因为当再次使用 let 关键字时,实际上是在创建一个新变量,所以可以更改值的类型,但重复使用相同的名称。

例如,假设我们的程序要求用户通过输入空格字符来显示他们想要在某些文本之间有多少个空格,然后我们希望将该输入存储为数字:

let spaces = " ";let spaces = spaces.len();

在上面的代码中, 第二行中,通过let重新定义spaces变量,用来储存第一个spaces变量的字节长度, 但是, 非常不推荐!

总结

本文主要介绍了Rust 语言中的mut、常量的用法, Rust在变量不可变性方面的设计比较奇特, 需要程序员们额外注意, 尤其是变量覆盖的特性, 在阅读代码时要留心。

本文的代码,已经同步到github, 有需要的可以去pull,Git地址: https://github.com/hintcnuie/learn_rust

0 阅读:0

查理谈科技

简介:感谢大家的关注