跳转至

Rust 的变量与类型

Caution

本页尚未完工

变量声明

Rust 语言中通过 let 关键字来声明变量。Rust 是一门静态强类型语言,因此任何一个变量都有一个确定、不可变的类型。如果在声明同时初始化,则可以依靠编译器的类型推断来得到变量类型,不一定需要显式指定类型。

1
2
3
4
let a: i32 = 1; // 完整的变量声明和初始化
let b: i32; // 显式声明类型,未初始化
let c = 1; // 声明同时初始化,类型由编译器推断为 i32
// let d; // 既没有初始化又没有标注类型,编译器无从得知类型,编译无法通过

与 C++ 的对照

Rust 的 let 关键字与 C++11 中引入的 auto 关键字有一些类似之处,例如在声明时初始化的变量可以自行推断类型。

可变性与不可变性

Rust 语言与许多其他语言不同的一点在于变量的默认不可变性,即变量的值默认是不可修改的。

1
2
let a = 1;
// a = 2; // 变量 a 默认不可变,编译无法通过

要声明一个可变的变量,需要加上 mut 关键字。

1
2
let mut a = 1;
a = 2; // 变量 a 可变,OK 

与 C++ 的对照

简单地看,let 声明的变量更类似于 C++ 中用 const 声明的常量,而 let mut 声明的变量更类似于“普通”的 C++ 变量。

这种说法是不严格的,但是这样思考可以快速上手。

如何打印一个变量

1
2
let a: i32 = 1;
println!("{}", a);

这里的 println! 是一个宏,但使用起来与函数很相似。

变量类型

基本类型

整数

Rust 中的整数有许多种,按照长度不同、有无符号进行区分。

Rust的多数整数命名遵循“字母 + 数字”这一结构。其中,以字母 i 开头的类型表示它是有符号整数,而以字母 u 开头的类型表示它是无符号整数,字母后面的数字则表示这一类型的长度,从最短的 8 字节到最长的 128 字节,有 i8, u8, i16, u16, i32, u32, i64, u64, i128, u128 这一系列类型。

编译器将整形字面量默认类型推导为 i32

1
2
let a = 1; // a 的类型为(隐式推导为)i32
let b: u32 = 1; // b 的类型(显式声明为)u32

与 C++ 的对照

C/C++ 数据模型 并没有给整形规定具体长度,只规定了不同整形之间长度的比较关系和最小长度。在常见的 64 位机器上,char, short, int, long long 分别表示 8、16、32 和 64 位带符号整数,分别可以对应 Rust 的 i8, i16, i32, i64,无符号整数类似。

在实际使用中,如果需要确定长度的整数,可以使用 cstdlib 中的 int32_t 等类型,它们在不同平台上有不同的 typedef,对应不同的具体类型。

C/C++ 没有统一的 128 位整数标准。

同时,Rust 还提供了两个特殊的类型 isizeusize,这两个类型的长度由平台决定,在 32 位平台上是 32 位,在 64 位平台上是 64 位。这一设计方便了内存中的寻址,如数组下标就接受 usize 而不是 u32 类型。

浮点数

类似整形,Rust 中的浮点型以字母 f 开头,后面是对应的长度,但只有 f32f64 两种类型。

编译器将浮点字面量默认类型推导为 f64

1
2
let f: f32 = 1.0;
let g = 2.0; // 类型默认推导为 f64

布尔值

Rust 中的布尔型为 bool,有且仅有两个值 truefalse

字符类型

Rust 中一个字符由单引号包裹,表示一个 Unicode 字符而非一个 ASCII 字符或字节

1
2
let ascii_char_z = 'z';
let heart_eyed_cat = '😻';

与 C++ 的对照

Rust 在设计时就考虑到了多语言支持,因此采取了这样的设计。如果希望与 C/C++ 中的 unsigned char 对应,即表示一个字节,应当使用 Rust 中的 u8 类型。

复合类型

元组

元组将几个相同或不同的类型组合为一个复合类型。

1
2
let tuple_1: (i32, f64, bool) = (1, 2.0, false); // 显式声明类型
let tuple_2 = (3, 1.0, true); // 隐式推断类型

元组支持通过模式匹配来解构,也可以通过点 . 来访问其元素。

1
2
3
4
5
6
7
let tuple = (1, 2.0, false);
let (x, y, z) = tuple;
// 上面的 let 语句创建了 i32 类型的变量 x、f64 类型的变量 y 和 bool 类型的变量 z
let xx = tuple.0;
let yy = tuple.1;
let zz = tuple.2;
// xx, yy, zz 分别对应 x, y, z

与 C++ 的对照

C++ 语言标准中并没有元组类型,但标准库中提供了功能和语义都相同的 std::tuple。但总的来说,C++ 的元组使用较为繁琐,不如 Rust 中作为基本类型的元组方便。

数组

Rust的数组将固定数目同类型变量储存在一起。

1
2
3
4
5
6
let arr_1: [i32; 5] = [1, 2, 3, 4, 5];
// [i32; 5] 表示包含5个 i32 的数组
let arr_2 = [1.0, 2.0, 3.0, 4.0];
// arr_2 的类型被隐式推断为 [f64; 4]
let arr_long: [i32; 100] = [0; 100];
// arr_long 的类型为 [i32; 100],而且这 100 个元素都是 0

如果需要一个可变长的数组,那么可以使用 Vec

与 C++ 的对照

Rust 的数组可以直接作为参数传递,这一点与 C/C++ 不同(C++ 中的 std::tuple 与 Rust 的数组更加类似)。与 C/C++ 类似的是,数组的长度都是不可变的,变长的线性容器在两门语言中分别叫做 std::Vecstd::vector

单元类型

Rust 中有一个特殊的单元类型 (Unit Type),它的类型是 (),而且它唯一的值也是 ()。例如,当一个表达式或函数什么也不返回时,它的返回类型和返回值就都是 ()

1
2
3
fn some_func() {
    return 0;
}

尝试编译上面的函数,编译器将报告类型不匹配,期望得到 () 而得到了整数。这表明无返回值的函数实质上返回了 ()

与 C++ 的对照

Rust 的 () 类似 C/C++ 的 void,但与之不完全相同。在开始理解时,可以这样对照;但请在认真探究 () 类型的设计时毫不留情地将有关 C/C++ 中的 void 的印象全部抛弃。

关于 Unit Type

在这里,我们不解释 Unit Type 为何设计成如此,也不深究它在 Rust 的其他地方有哪些用处。如果对 Unit Type 的细节感兴趣,可以参考 Rust 官方文档或者查看 这个 StackOverFlow 问题


最后更新: 2022年1月17日
作者: Ashitemaru (15.12%), ChrisZhang (84.88%)