• rust变量与常量


    变量绑定

    rust里有个核心原则,那就是所有权。在其它语言中,我们可以把一个值赋值给变量。但是在rust里,是把值绑定到变量上。任何内存对象都是有主人的,而且一般情况下完全属于它的主人,绑定就是把这块内存绑定给一个变量,让这个变量成为它的主人。

    不可变变量

    在rust里,默认情况下变量是不可变的。一旦变量被声明为不可变,那么一旦为它绑定值,就不能再修改。变量的声明方式如下所示:

    let variable_name:type = value
    
    • 1

    let是rust声明变量的关键字。下面是一个例子:

    fn main() {
        let num = 123;          
        println!("{}", num);    // {}是占位符,类似与C/C++中的%d, %s等
    }
    
    • 1
    • 2
    • 3
    • 4

    示例中声明了一个不可变变量num,绑定的值是123。如果尝试对num进行修改,会报错。例如:

    fn main() {
        let num = 123;
        println!("{}", num);
        num = 456;              // 错误,不能对不可变变量进行二次赋值。
        println!("{}", num);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用cargo check来进行语法检查,报错如下所示:
    在这里插入图片描述

    类型声明

    如果你仔细观察你的vscode编辑器,就会发现如下所示的细节。
    在这里插入图片描述
    i32是rust的基本数据类型,这里是rust编译器帮我们进行了类型推断,将num设置为i32类型。大多数情况下,rust编译器都可以帮助我们进行类型的推断,但是偶尔也无法推断,这时候我们就要手动声明变量的数据类型了。例如:在这里插入图片描述
    rust编译器帮助我们推断的类型是i32,但是你使用cargo check检查的时候,就会发现有语法错误。rust编译器非常智能的提示你,i32的范围是-2147483648到2147483647,而111111111111不在这个范围内,并且提示你可以使用i64类型替代i32类型。让我们将i32改为i64,再执行cargo check。
    在这里插入图片描述
    可以看到,成功通过了语法检查。在现在的版本(rust1.65),可以通过下面的方式来获取数据类型的名称。

    fn print_type_of<T>(_: &T) {
        println!("{}", std::any::type_name::<T>())
    }
    
    • 1
    • 2
    • 3

    例如:

    fn print_type_of<T>(_: &T) {
        println!("{}", std::any::type_name::<T>())
    }
    
    fn main() {
        let s = "Hello";
        let i = 42;
        print_type_of(&s); // &str
        print_type_of(&i); // i32
        print_type_of(&main); // playground::main
        print_type_of(&print_type_of::<i32>); // playground::print_type_of
        print_type_of(&{ || "Hi!" }); // playground::main::{{closure}}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    官方文档显示该函数是不稳定版本的,后续可能会发生变化。

    可变变量

    由于rust定义的变量默认情况下是不可变变量。可以通过 mut 关键字让变量成为可变变量。例如:

    fn main() {
        let mut a = 123;
        println!("{}", a);
        a = 456;
        println!("{}", a);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用mut修饰之后的变量a,可以在后续的过程中进行重新绑定。这段代码执行的结果是:

    123
    456
    
    • 1
    • 2

    变量默认是不可变的这一点,是 Rust 提供给你的众多优势之一,让你得以充分利用 Rust 提供的安全性和简单并发性来编写代码。

    使用下划线开头忽略未使用的变量

    如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告(不是error,这点和go语言不一样),因为这可能会是个 BUG。如果你需要暂时忽略掉这个警告,可以让变量以下划线开头,例如:

    fn main() {
        let a = 1;
        let b = 2;
        println!("{}", a);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    使用cargo run执行这段代码,输出如下所示:
    在这里插入图片描述
    rust这个编译器是真的非常智能,它警告你,未使用变量b并且给出了解决方案,在b前面加上前缀_即可。让我们加上前缀在来试试。
    在这里插入图片描述
    rust对于未使用的变量只是给出警告,而不像go语言那样未被使用的变量是会报错的。只是个警告这一点非常方便我们进行调试,如果在go语言中,就不太方便调试。

    变量遮蔽(shadowing)

    Rust 允许声明相同的变量名,在后面声明的变量会遮蔽掉前面声明的。例如:

    use::std::io;
    
    fn print_type_of<T>(_: &T) {
        println!("{}", std::any::type_name::<T>())
    }
    
    fn main() {
        println!("Please input a number:");     // 输出提示
        
        let mut num = String::new();    // 定义一个名为num的String类型的可变变量
        io::stdin().read_line(&mut num).expect("Failed to read line");  // 从标准输入读取一行内容,保存到num中
        print_type_of(&num);            // 打印数据类型
        let num: i32 = match num.trim().parse() {   // 将String类型的num转为i32类型的num,注意这里使用let再次声明num,并让num绑定一个i32数据
            Ok(num) => num,
            Err(_) => {
                 println!("请输入整数,请勿输入无关符号!");
                 return;
            }
        };
        print_type_of(&num);            // 打印数据类型
        println!("number is {num}");    // 输出数字。
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    执行上面这段代码,打印结果如下所示:
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/7ebd6e1028ef4f3cbd27f544cb841867.gi
    可以看到,前后两次num的数据类型发生了变化。这段代码先将一个空字符串(String::new())绑定到变量num上,因此我们第一次打印变量num的数据类型是“alloc::string::String”,接下来,代码从标准输入读取一行内容存放到num中(修改了原来的空字符串)。接着将字符串num转为i32值然后重新绑定到num上,这就导致num的数据类型变为了i32。

    像上面的场景,从标准输入读取的数字是字符串类型,但是我希望它是i32类型。变量覆盖避免了像num_str这样的名称,你可以重新绑定到新的值上,而不必去给变量起一个复杂的名称;也避免了大量含义不明的tmp变量或者是xxx_tmp变量这样的名称。

    变量遮蔽会涉及一次内存对象的在分配,而不像mut变量那样,它是在原来的内存上做修改。

    常量

    变量的值不能更改可能让你想起其他另一个很多语言都有的编程概念:常量(constant)。与不可变变量一样,常量也是绑定到一个常量名且不允许更改的值,但是常量和变量之间存在一些差异:

    • 常量不允许使用 mut。常量不仅仅默认不可变,而且自始至终不可变,因为常量在编译完成后,已经确定它的值。(因此,rust里的常量和C/C++中的宏非常类似,它要求在编译期是确定的,而不可变变量的值可以在运行时确定。)
    • 常量使用 const 关键字而不是 let 关键字来声明,并且值的类型必须标注。

    Rust 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词,另外对数字字面量可插入下划线以提高可读性,如果未遵守命名规范,rust会给出警告信息。如下所示:
    在这里插入图片描述
    警告信息告诉我们应该将常量max_num变为大写的名称,并且在给出了帮助信息,提示我们将max_num改为MAX_NUM。rust编译器真的非常友好。

    命名约定

    在“rust变量与常量”这篇文章中,提到过rust的常量名应该使用全部大写的方式。实际上,rust有一个命名约定文档————RFC430,其中描述了命名约定。

    通常,对于 type-level 的构造 Rust 倾向于使用驼峰命名法,而对于 value-level 的构造使用蛇形命名法。详情如下:

    ItemConvention
    Cratessnake_case (but prefer single word)
    Modulessnake_case
    TypesUpperCamelCase
    TraitsUpperCamelCase
    Enum variantsUpperCamelCase
    Functionssnake_case
    Methodssnake_case
    General constructorsnew or with_more_details
    Conversion constructorsfrom_some_other_type
    Local variablessnake_case
    Static variablesSCREAMING_SNAKE_CASE
    Constant variablesSCREAMING_SNAKE_CASE
    Type parametersconcise UpperCamelCase, usually single uppercase letter: T
    Lifetimesshort, lowercase: 'a
    • 对于驼峰命名法,复合词的缩略形式我们认为是一个单独的词语,所以只对首字母进行大写:使用 Uuid 而不是 UUID,Usize 而不是 USize,Stdin 而不是 StdIn。
    • 对于蛇形命名法,缩略词用全小写:is_xid_start。
    • 对于蛇形命名法(包括全大写的 SCREAMING_SNAKE_CASE),除了最后一部分,其它部分的词语都不能由单个字母组成: btree_map 而不是 b_tree_map,PI_2 而不是 PI2.
    • 包名不应该使用 -rs 或者 -rust 作为后缀,因为每一个包都是 Rust 写的,因此这种多余的注释其实没有任何意义。

    参考资料

    Rust圣经
    Rust RFC430

  • 相关阅读:
    PDF格式分析(七十)——注释边框样式及外观流
    WebGL实现简易的局部“马赛克”
    Day06-Java面向对象基础之类和对象
    安装rabbitMQ踩的坑[通用版]
    Redis设计与实现笔记 - 数据结构篇
    LeetCode 27. 移除元素
    [附源码]计算机毕业设计学习互助辅助系统Springboot程序
    web基础与HTTP协议
    分享好用的Cloudflare测速文件
    二刷算法训练营Day14 | 二叉树(1/9)
  • 原文地址:https://blog.csdn.net/zy010101/article/details/127965152