Rust 中的变量(特性)——Rust语言基础05

1. Rust 中的变量

我们从上一篇文章中了解到在 Rust 中定义的变量默认情况下是不可变的。这是 Rust 语言为保证其安全性和简单并发性方式的代码而规定的特性。Rust 考虑的是,在设计程序时,假设代码刚开始定义一个值,并且该值永远也不会改变,但是当代码多了后,再另一部分忘记了刚开始的设想,又改变了那个值,那么将可能导致之前的代码不会按预期的效果执行下去。那么 Rust 就会改变这一现状,当你声明一个值且不会再改变它时,它就真的不能再改变,Rust 编译器会帮你去保证这一点,从而使你构建的代码更容易阅读和推理。

在这里插入图片描述

2. Rust 的默认不可变变量(immutable)

和往常一样,我们使用 Cargo 新建一个项目,用来做相关的实验。创建成功后的目录结构如下。

imaginemiracle:rust_projects$ cargo new variables
imaginemiracle:rust_projects$ cd variables/
imaginemiracle:variables$ tree
.
├── Cargo.toml
└── src
    └── main.rs

1 directory, 2 files

打开 src/main.rs 源文件,并将代码更新为下面代码。

fn main() {

    let val = 27149;   // 陈永仁警号——《无间道》

    println!("The value of val is: {val}");

    val = 4927;        // 刘建明警号——《无间道》

    println!("The value of val is: {val}");

}

这里我们尝试对 Rust 的默认变量做修改。OK!来运行一下看看会发生什么。

imaginemiracle:variables$ cargo run
   Compiling variables v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/variables)
error[E0384]: cannot assign twice to immutable variable `val`
 --> src/main.rs:7:5
  |
3 |     let val = 27149;   // 陈永仁警号——《无间道》
  |         ---
  |         |
  |         first assignment to `val`
  |         help: consider making this binding mutable: `mut val`
...
7 |     val = 4927;        // 刘建明警号——《无间道》
  |     ^^^^^^^^^^ cannot assign twice to immutable variable

For more information about this error, try `rustc --explain E0384`.
error: could not compile `variables` due to previous error

编译器报错:cannot assign twice to immutable variable
这里可以看到编译器告诉我们 “不能对不可变变量赋值两次,要不要将变量声明为 mut 的可变变量”。

3. Rust 的可变变量(mutable)

既然默认的变量是不可改变的,那么将如何让变量可变呢?关于这一点,若是阅读过本系列第 4 篇文章的伙伴应该已经清楚了。我们需要在变量名前添加 mut 关键字来说明此变量将被声明为可变变量 (mutable)
让我们将上面的 src/main.rs 修改为下面代码:

fn main() {

    let mut val = 27149;   // 陈永仁警号——《无间道》

    println!("The value of val is: {val}");

    val = 4927;        // 刘建明警号——《无间道》

    println!("The value of val is: {val}");

    println!("对不起,我是警察。");

}

那么这个时候再次运行该程序呢:

imaginemiracle:variables$ cargo run
   Compiling variables v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.29s
     Running `target/debug/variables`
The value of val is: 27149
The value of val is: 4927
对不起,我是警察。

Oh,我们已经正确的改变了可变变量的值。
[注]:在 Rust 中,变量的可变性,取决于开发人员。

4. Rust 的常量(Constants)

CC++ 基础的朋友一定清楚什么是常量。Rust 中也有常量(const),被声明为常量后,那么程序将不允许再更改该常量的值,这里看起来与 Rust 中的默认变量特性很相似,都是不允许被修改的,但两者也存在一定的差异。
差别如下:

  • 首先要明确的一点是,const 修饰的常量是不允许使用 mut 的,即常量必须保证自己是不可变的,这一点是不可违反的;
  • 常量使用 const 关键字修饰,而默认的不可变变量由 let 关键字修饰;
  • 使用 const 修饰声明的常量必须注明其数据类型,而 let 则可以不显示注明,依靠 Rust 编译器自动识别变量类型;
  • 常量只能赋值为常量表达式,而非仅再运行时计算的数值。

下面是一个常量声明的例子:

fn main() {
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
}

常量的名称为 THREE_HOURS_IN_SECONDS,其值赋值为 60(一分钟的秒数)乘以 60(一小时的分钟数)乘以 3(需要计算的小时数)。

另外一点需要注意的是:Rust 中的常量命名规则,需使用全大写的单词,之间需要用下划线间隔。

编译器能够在编译时评估一组有限的操作,这让我们可以选择以更容易理解和验证的方式写出这个值,而不是将此常量设置为值 10,800

这样做的好处是可以更清楚的将开发时的常量含义传达给代码的 “未来守护者”。

4.1. 不惯着他会怎样

有时候我们总会有种 “杠精” 想法(不,我们这是为了更透彻的理解)。

(1) 如果我非要在 const 修饰的常量前加上 mut 会怎样?

(2) 要是我在声明常量时没有注明数据类型会怎样?

(3) 如果我就是要在 Rust 种修改 const 修饰的常量怎么办?

(4) 如果我不按你规定的规则命名常量会怎样?

来吧,让我们逐一的做实验,让实践验证我们的杠精式疑虑。
验证 (1):
用下面代码验证

fn main() {

    const mut THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
}

尝试执行:

imaginemiracle:variables$ cargo run
   Compiling variables v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/variables)
error: const globals cannot be mutable
 --> src/main.rs:3:11
  |
3 |     const mut THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
  |     ----- ^^^ cannot be mutable
  |     |
  |     help: you might want to declare a static instead: `static`

error: could not compile `variables` due to previous error

OK!编译器直接说常量式不可以变成可变变量。(编译器不会让过。)

验证 (2):
用下面代码验证

fn main() {

    const THREE_HOURS_IN_SECONDS = 60 * 60 * 3;
}

尝试执行:

imaginemiracle:variables$ cargo run
   Compiling variables v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/variables)
error: missing type for `const` item
 --> src/main.rs:3:11
  |
3 |     const THREE_HOURS_IN_SECONDS = 60 * 60 * 3;
  |           ^^^^^^^^^^^^^^^^^^^^^^ help: provide a type for the constant: `THREE_HOURS_IN_SECONDS: i32`

error: could not compile `variables` due to previous error

OK !编译器说 missing type for 'const' item,检测到我们的语法有问题,说 const 这块缺少类型。(编译器不让过。)

验证 (3):
用下面代码验证

fn main() {

    const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

    THREE_HOURS_IN_SECONDS = 12;
}

尝试执行:

imaginemiracle:variables$ cargo run
   Compiling variables v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/variables)
error[E0070]: invalid left-hand side of assignment
 --> src/main.rs:6:28
  |
6 |     THREE_HOURS_IN_SECONDS = 12;
  |     ---------------------- ^
  |     |
  |     cannot assign to this expression

For more information about this error, try `rustc --explain E0070`.
error: could not compile `variables` due to previous error

OK !编译器不允许这样赋值。(编译器不让过。)

验证 (4):
用下面代码验证

fn main() {

    const three_HoursIn_seconds: u32 = 60 * 60 * 3;

    println!("The result: {three_HoursIn_seconds}");
}

尝试执行:

imaginemiracle:variables$ cargo run
   Compiling variables v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/variables)
warning: constant `three_HoursIn_seconds` should have an upper case name
 --> src/main.rs:4:11
  |
4 |     const three_HoursIn_seconds: u32 = 60 * 60 * 3;
  |           ^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to upper case: `THREE_HOURS_IN_SECONDS`
  |
  = note: `#[warn(non_upper_case_globals)]` on by default

warning: `variables` (bin "variables") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 0.20s
     Running `target/debug/variables`
The result: 10800

哎哟喂 ~,编译器只是警告了一下我们,但是要注意的是,编译器提示我们说,常量还是应该有个全大写的名字。(尊重一下他老人家吧,就规规矩矩的吧。)

5. 覆盖(Shadowing)

Rust 中允许声明一个与先前变量同名的新变量,此时第一个变量则被第二个新的变量覆盖(Shadowing),这将意味着从第二个新声明的变量以后,编译器再看到的同样名称的变量将发生改变(并非存在两个同名变量)。覆盖(Shadowing)了的变量也有生命周期的限制。下面我们看一个例子(将 src/main.rs 内容修改为如下代码):

fn main() {

    let num = 2048;

    let num = num / 2;	// shadowing

    {
        let num = num / 2;

        println!("The value of num in the inner scope is: {num}");

    } // 使用 {} 设定变量声明周期

    println!("The value of num is: {num}");

}

分析:
该段代码首先为 num 绑定一个数值为 2048。接着再通过 let num 重复创建了一个新的变量 num,并为其赋值为原始值除以 2,那么此时的 num 的值应该为 1024

之后在一个大括号限制的范围内再次使用 let num 覆盖并创建了一个新的 num,赋值为之前的值除以 2,这里的新创建的 num 的声明周期到大括号结束也随之结束,那么 shadowing 的效果也随之消失。在大括号之外再次查看 num 的值将会恢复到大括号开始时的状态。

让我们来执行看看分析是否正确:

imaginemiracle:variables$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/variables`
The value of num in the inner scope is: 512
The value of num is: 1024

Boy: おかしいなあ,这样看起来也是在修改变量值呀!
Girl: 是的,这样的确看起来像是在改变变量的值。
Boy: ???Rust 干嘛这么费劲还要折腾一个 mut 哇!

事实上 mutshadowing 过程还是有的区别的,使用 let 重新声明并覆盖掉之前的变量,该过程是的的确确的创建了一个全新的变量,因此我们可以任意的更换白能量的类型,但使用相同的名字。让我们通过一个例子说明(将 src/main.rs 内容修改为如下代码):

fn main() {

    let hello = "hello world!";

    println!("The string is: {hello}");

    let hello = hello.len();

    println!("The string length is: {hello}");

}

分析:
来看这段代码,首先声明了一个名为 hello 的变量,为其绑定的值为 “hello world!”,这里并没有显式的注明其数据类型,因为 Rust 编译器会自动识别它为字符串类型。而在下面使用 let hello 重新声明了一个同名变量,为其绑定的值为原 hello 变量的字符串长度,同样没有显式注明数据类型,编译器会自动检测为整型(Rust 中默认整数类型为 i32)。那么这里 hello 实际上已经不仅改变了值,其代表的数据类型也发生了改变,除了名称未改变,其它的已经算是完全的改头换面了。

该段代码的执行结果如下:

imaginemiracle:variables$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/variables`
The string is: hello world!
The string length is: 12

那么如果使用 mut 会发生什么情况呢?来看看。

(将 src/main.rs 内容修改为如下代码):

fn main() {

    let mut hello = "hello world!";

    println!("The string is: {hello}");

    hello = hello.len();

    println!("The string length is: {hello}");

}

尝试执行它:

imaginemiracle:variables$ cargo run
   Compiling variables v0.1.0 (/home/imaginemiracle/Miracle/Code/rust_projects/variables)
error[E0308]: mismatched types
 --> src/main.rs:7:13
  |
3 |     let mut hello = "hello world!";
  |                     -------------- expected due to this value
...
7 |     hello = hello.len();
  |             ^^^^^^^^^^^ expected `&str`, found `usize`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables` due to previous error

编译器将会报错,提示我们类型不匹配,无法将整型赋值给字符串类型的变量。

#Review

OK ! 到目前为止我们已经清楚的了解了如何声明变量以及,变量是如何工作的。接下来将要看看 Rust 中更多的数据类型。


Boys and Girls!!!
准备好了吗?下一节我们要开始做个小练习了哦!

不!我还没准备好,让我先回顾一下之前的。
上一篇《Rust 编写猜字谜游戏——Rust语言基础04》

我准备好了,掛かって来い(放马过来)!
下一篇《Rust 中的基本数据类型——Rust语言基础06》


觉得这篇文章对你有帮助的话,就留下一个赞吧v*
请尊重作者,转载还请注明出处!感谢配合~
[作者]: Imagine Miracle
[版权]: 本作品采用知识共享署名-非商业性-相同方式共享 4.0 国际许可协议进行许可。
[本文链接]: https://blog.csdn.net/qq_36393978/article/details/125526797

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Imagine Miracle

爱你哟 =^ v ^=

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值