Rust 是一种静态类型(statically typed)的语言,这意味着它必须在编译期知道所有变量的类型。编译器通常可以根据值和使用方式推导出我们想要使用的类型。在类型可能是多种情况时,例如在 [[2 猜数游戏]]中当我们使用 parse
将 String
转换成数值类型时,我们必须加上一个类型标注,如下所示:
let guess: u32 = "42".parse().expect("Not a number!");
Rust 的每个值都有确切的数据类型(data type),该类型告诉 Rust 数据是被指定成哪类数据,从而让 Rust 知道如何使用该数据。
标量类型
标量(scalar)类型表示单个值。Rust 有 4 个基本的标量类型:整型、浮点型、布尔型和字符。
整数类型
整数(integer)是没有小数部分的数字。有符号和无符号表示数字能否取负数,有符号整型以 i
开始,i
是英文单词 integer 的首字母,与之相反的是 u
,代表无符号 unsigned
类型。表 3-1 显示了 Rust 中的内置的整数类型。我们可以使用这些定义形式中的任何一个来声明整数值的类型。
长度 | 有符号类型 | 无符号类型 |
---|---|---|
8 位 | i8 | u8 |
16 位 | i16 | u16 |
32 位 | i32 | u32 |
64 位 | i64 | u64 |
128 位 | i128 | u128 |
arch | isize | usize |
每个有符号类型规定的数字范围是 -(2n - 1) ~ 2n - 1 - 1,其中 n
是该定义形式的位长度。所以 i8
可存储数字范围是 -(27) ~ 27 - 1,即 -128 ~ 127。无符号类型可以存储的数字范围是 0 ~ 2n - 1,所以 u8
能够存储的数字为 0 ~ 28 - 1,即 0 ~ 255。
此外,isize
和 usize
类型取决于程序运行的计算机体系结构,在表中表示为“arch”:若使用 64 位架构系统则为 64 位,若使用 32 位架构系统则为 32 位。
可按表 3-2 中所示的任意形式来编写整型的字面量。注意,可能属于多种数字类型的数字字面量允许使用类型后缀来指定类型,例如 57u8
。数字字面量还可以使用 _
作为可视分隔符以方便读数,如 1_000
,此值和 1000
相同。
数字字面量 | 示例 |
---|---|
十进制 | 98_222 |
十六进制 | 0xff |
八进制 | 0o77 |
二进制 | 0b1111_0000 |
字节 (仅限于 u8 ) | b'A' |
浮点类型
浮点数(floating-point number)是带有小数点的数字,在 Rust 中浮点类型(简称浮点型)数字也有两种基本类型。Rust 的浮点型是 f32
和 f64
,它们的大小分别为 32 位和 64 位。默认浮点类型是 f64
,因为在现代的 CPU 中它的速度与 f32
的几乎相同,但精度更高。所有浮点型都是有符号的。
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
Rust 的所有数字类型都支持基本数学运算:加法、减法、乘法、除法和取模运算。整数除法会向下取整。[[附录:运算符与符号]]罗列了 Rust 提供的所有运算符的列表。
布尔类型
和大多数编程语言一样,Rust 中的布尔类型也有两个可能的值:true
和 false
。布尔值的大小为 1 个字节。Rust 中的布尔类型使用 bool
声明。例如:
fn main() {
let t = true;
let f: bool = false; // with explicit type annotation
}
字符类型
Rust 的 char
(字符)类型是该语言最基本的字母类型,下面是一些声明 char
值的例子:
fn main() {
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '😻';
}
注意声明的 char
字面量采用单引号括起来,这与字符串字面量不同,字符串字面量是用双引号括起来。
Rust 的字符类型大小为 4 个字节,表示的是一个 Unicode 标量值,这意味着它可以表示的远远不止是 ASCII。标音字母,中文/日文/韩文的文字,emoji,还有零宽空格(zero width space)在 Rust 中都是合法的字符类型。Unicode 值的范围为 U+0000
~ U+D7FF
和 U+E000
~U+10FFFF
。
复合类型
复合类型(compound type)可以将多个值组合成一个类型。Rust 有两种基本的复合类型:元组(tuple)和数组(array)。
元组类型
元组是将多种类型的多个值组合到一个复合类型中的一种基本方式。元组的长度是固定的:声明后,它们就无法增长或缩小。
我们通过在小括号内写入以逗号分隔的值列表来创建一个元组。元组中的每个位置都有一个类型,并且元组中不同值的类型不要求是相同的。下面的示例中添加了可选的类型标注:
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
想从元组中获取个别值,我们可以使用模式匹配或索引,如下所示:
fn main() {
let tup = (500, 6.4, 1);
// 方法1: 模式匹配
let (x, y, z) = tup;
// 方法2: 索引
let five_hundred = tup.0;
let six_point_four = tup.1;
let one = tup.2;
}
没有任何值的元组
()
是一种特殊的类型,只有一个值,也写成()
。该类型被称为单元类型(unit type),该值被称为单元值(unit value)。如果表达式不返回任何其他值,就隐式地返回单元值。
数组类型
将多个值组合在一起的另一种方式就是使用数组(array)。与元组不同,数组的每个元素必须具有相同的类型。与某些其他语言中的数组不同,Rust 中的数组具有固定长度。
我们在方括号内以逗号分隔的列表形式将值写到数组中:
fn main() {
let a = [1, 2, 3, 4, 5];
}
当你希望将数据分配到栈(stack)而不是堆(heap)时,或者当你希望确保始终具有固定数量的元素时,数组特别有用。而当需要动态地调整元素的数量时,vector 类型会更加灵活。vector 类型类似于标准库中提供的集合类型,其大小允许增长或缩小。
指定数组中的元素类型
let a: [i32; 5] = [1, 2, 3, 4, 5];
这里,i32
是每个元素的类型。分号之后,数字 5
表明该数组包含 5 个元素。
生成重复数据的数组
let a = [3; 5];
变量名为 a
的数组将包含 5
个元素,这些元素的值初始化为 3
。这种写法与 let a = [3, 3, 3, 3, 3];
效果相同,但更简洁。
访问数组元素
数组是可以在栈上分配的已知固定大小的单个内存块。可以使用索引访问数组的元素,如下所示:
fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
}