• 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

    fn main() {
    
        let val = 27149;   // 陈永仁警号——《无间道》
    
        println!("The value of val is: {val}");
    
        val = 4927;        // 刘建明警号——《无间道》
    
        println!("The value of val is: {val}");
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这里我们尝试对 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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    编译器报错: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!("对不起,我是警察。");
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    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
    对不起,我是警察。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    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;
    }
    
    • 1
    • 2
    • 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;
    }
    
    • 1
    • 2
    • 3
    • 4

    尝试执行:

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

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

    fn main() {
    
        const THREE_HOURS_IN_SECONDS = 60 * 60 * 3;
    }
    
    • 1
    • 2
    • 3
    • 4

    尝试执行:

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

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

    fn main() {
    
        const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
    
        THREE_HOURS_IN_SECONDS = 12;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    尝试执行:

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

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

    fn main() {
    
        const three_HoursIn_seconds: u32 = 60 * 60 * 3;
    
        println!("The result: {three_HoursIn_seconds}");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    尝试执行:

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

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

    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}");
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    分析:
    该段代码首先为 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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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}");
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    分析:
    来看这段代码,首先声明了一个名为 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
    
    • 1
    • 2
    • 3
    • 4
    • 5

    那么如果使用 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}");
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    尝试执行它:

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    #Review

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


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

    不!我还没准备好,让我先回顾一下之前的。
    上一篇《Rust语言——小小白的入门学习04》

    我准备好了,掛かって来い(放马过来)!
    下一篇《Rust语言——小小白的入门学习06》


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

  • 相关阅读:
    基于粒子群算法的排课系统,基于PSO的排课系统,粒子群算法原理,粒子群算法流程
    自动驾驶和辅助驾驶系统的概念性架构(一)
    MySQL 事务的简单理解
    企业微信H5开发遇到的坑
    谷粒商城-day13-es和商品上架
    『德不孤』Pytest框架 — 12、Pytest中Fixture装饰器(二)
    电子产品出口日本站PSE认证和METI备案需要哪些资料?
    5.Vue中的计算属性(compute)监视属性(watch),二者优点和对比
    2023.11.17 关于 Spring Boot 日志文件
    1.servlet规范简单整理
  • 原文地址:https://blog.csdn.net/qq_36393978/article/details/125526797