变量
# 1 变量和可变性
变量默认是不可改变的(immutable)。
fn main() {
let x = 5;
println!("The value of x is: {x}");
x = 6;//错误
println!("The value of x is: {x}");
}
2
3
4
5
6
可以在变量名前添加 mut
来使其可变。
fn main() {
let mut x = 5;
println!("The value of x is: {x}");
x = 6;
println!("The value of x is: {x}");
}
2
3
4
5
6
常量: 类似于不可变变量,常量 (constants) 是绑定到一个名称的不允许改变的值,不过常量与变量还是有一些区别。
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
# 2 数据类型
在 Rust 中,每一个值都有一个特定 数据类型(data type),这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。我们将看到两类数据类型子集:标量(scalar)和复合(compound)。
# 2.1 标量类型
标量(scalar)类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。你可能在其他语言中见过它们。让我们深入了解它们在 Rust 中是如何工作的。
# 2.1.1 整型
整型 是一个没有小数部分的数字。我们在第二章使用过 u32
整数类型。该类型声明表明,它关联的值应该是一个占据 32 比特位的无符号整数(有符号整数类型以 i
开头而不是 u
)。表格 3-1 展示了 Rust 内建的整数类型。我们可以使用其中的任一个来声明一个整数值的类型。
表格 3-1: Rust 中的整型
长度 | 有符号 | 无符号 |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
架构相关 | isize | usize |
每一个变体都可以是有符号或无符号的,并有一个明确的大小。
# 2.1.2 浮点型
Rust 也有两个原生的 浮点数(floating-point numbers)类型,它们是带小数点的数字。Rust 的浮点数类型是 f32
和 f64
,分别占 32 位和 64 位。默认类型是 f64
,因为在现代 CPU 中,它与 f32
速度几乎一样,不过精度更高。所有的浮点型都是有符号的。
fn main() {
let x = 2.0; // f64
let y: f32 = 3.0; // f32
}
2
3
4
5
浮点数采用 IEEE-754 标准表示。(f32
是单精度浮点数,f64
是双精度浮点数。)
# 2.1.3 布尔类型
正如其他大部分编程语言一样,Rust 中的布尔类型有两个可能的值:true
和 false
。Rust 中的布尔类型使用 bool
表示。例如:
fn main() {
let t = true;
let f: bool = false; // with explicit type annotation
}
2
3
4
5
# 2.1.4 字符类型
Rust 的 char
类型是语言中最原始的字母类型。下面是一些声明 char
值的例子:
fn main() {
let c = 'z';
let z: char = 'ℤ'; // with explicit type annotation
let heart_eyed_cat = '😻';
}
2
3
4
5
# 2.2 复合类型
复合类型(Compound types)可以将多个值组合成一个类型。Rust 有两个原生的复合类型:元组(tuple)和数组(array)。
# 2.2.1 元组类型
元组是一个将多个不同类型的值组合进一个复合类型的主要方式。元组长度固定:一旦声明,其长度不会增大或缩小。
我们使用包含在圆括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。这个例子中使用了可选的类型注解:
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
2
3
tup
变量绑定到整个元组上,因为元组是一个单独的复合元素。为了从元组中获取单个值,可以使用模式匹配(pattern matching)来解构(destructure)元组值,像这样:
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {y}");
}
2
3
4
5
6
7
程序首先创建了一个元组并绑定到 tup
变量上。接着使用了 let
和一个模式将 tup
分成了三个不同的变量,x
、y
和 z
。这叫做 解构(destructuring),因为它将一个元组拆成了三个部分。最后,程序打印出了 y
的值,也就是 6.4
。
我们也可以使用点号(.
)后跟值的索引来直接访问所需的元组元素。例如:
fn main() {
let x: (i32, f64, u8) = (500, 6.4, 1);
let five_hundred = x.0;
let six_point_four = x.1;
let one = x.2;
}
2
3
4
5
6
7
8
9
这个程序创建了一个元组,x
,然后使用其各自的索引访问元组中的每个元素。跟大多数编程语言一样,元组的第一个索引值是 0。
不带任何值的元组有个特殊的名称,叫做 单元(unit) 元组。这种值以及对应的类型都写作 ()
,表示空值或空的返回类型。如果表达式不返回任何其他值,则会隐式返回单元值。
# 2.2.2 数组类型
另一个包含多个值的方式是 数组(array)。与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,Rust 中的数组长度是固定的。
我们将数组的值写成在方括号内,用逗号分隔的列表:
fn main() {
let a = [1, 2, 3, 4, 5];
}
2
3
可以像这样编写数组的类型:在方括号中包含每个元素的类型,后跟分号,再后跟数组元素的数量。
let a: [i32; 5] = [1, 2, 3, 4, 5];
这里,i32 是每个元素的类型。分号之后,数字 5 表明该数组包含五个元素。
你还可以通过在方括号中指定初始值加分号再加元素个数的方式来创建一个每个元素都为相同值的数组:
let a = [3; 5];
变量名为 a
的数组将包含 5
个元素,这些元素的值最初都将被设置为 3
。这种写法与 let a = [3, 3, 3, 3, 3];
效果相同,但更简洁。
# 访问数组元素
数组是可以在栈 (stack) 上分配的已知固定大小的单个内存块。可以使用索引来访问数组的元素,像这样:
fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
}
2
3
4
5
6
在这个例子中,叫做 first
的变量的值是 1
,因为它是数组索引 [0]
的值。变量 second
将会是数组索引 [1]
的值 2
。