复合类型
复合类型可以将多个基本类型组合了一个类型中,Rust有两种基本的组合类型:元组与数组
元组类型
一个元组是将不同类型的数值 组合成复合类型的方法。元组有固定的长度:一旦声名,它的大小不能缩放。
我们在小括号中通过逗号分隔的的数字进行声名。每一个数字在元组中都有类型,不同的类型在元组中类型可以不同。不同类型在同一元组中需要为每个数值 声名类型。
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
}
tup变量代表整个元组,一个元组被认为是一个组合类型。为了从元组中获取单个值,我们可以使用模式匹配去分别赋到对应变量中
fn main() {
let tup = (500, 6.4, 1);
let (x, y, z) = tup;
println!("The value of y is: {y}");
}
此段小程序首先创建了一个元组,然赋值组tup变量。然后使用模式将tup中的值分别取出赋值给单个的x,y,z变量。这就叫作解构元组的,因为它将元组打散为部分,最终此程序打印出y的值。
我们也可以通过.(点运算符)直接访问元组中的元素,点号后面跟的是元素索引。
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;
}
此程序创建了元组x,然通过索引访问其中的元素。同其他编程语言一样,首元素的索引为0
没有元素的元组有一个特别的名字叫unit。此值和响应类型可写成()并且代表一个空值 或空的返回。如是不必返回任何值,可以隐式返回unit.
数组类型
另外一个多值集合的方法是数组。与元组不同,数组中的每个元素的类型必须相同。与其他编程语言中的数组不同,Rust中的数组有固定长度。
数组是以中括号引起来的元素集合,元素之间用逗号分隔。
fn main() {
let a = [1, 2, 3, 4, 5];
}
如果你想让你的数据被分配到栈上而不是堆上,数组比较有用(后面我们将更多讨论栈和堆),然而你必须总是确定数组的长度。一个数组不能像矢量(vector)类型那么灵活。一个Vector是标准库提供的一个集合类型,vector是可以增长与收缩大小的。如果你不确定是使用数组还是vector,你应该选择vector。第八章会讨论vector的更多信息。
然而,在你知道元素个数时,数组是有用的且不需要改变大小。例如,如在程序中使用月份的名子,你一定会用数组而不是vector,因为你知道一年包括12个月,
let months = ["January", "February", "March", "April", "May", "June", "July",
"August", "September", "October", "November", "December"];
你使用方括号定义一个数组类型,分号分隔类型和长度,代码如下,
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];
访问数组元素
数组是一个单块栈内存,这里指内存空间是连续的,固定大小可以在栈上分配。可以使用索引访问元素。
fn main() {
let a = [1, 2, 3, 4, 5];
let first = a[0];
let second = a[1];
}
在此例中,first变量的值为数组a的第一个元素,second为数组中的第二个元素。
无效的元素访问
如果你访问超过数组长度的索引元素,我们看一下会发生什么。你运行下面的第二章的代码,你输入一个超过数组长度的索引。
use std::io;
fn main() {
let a = [1, 2, 3, 4, 5];
println!("Please enter an array index.");
let mut index = String::new();
io::stdin()
.read_line(&mut index)
.expect("Failed to read line");
let index: usize = index
.trim()
.parse()
.expect("Index entered was not a number");
let element = a[index];
println!("The value of the element at index {index} is: {element}");
}
这个代码可以正常编译。你用cargo run运行这段代码,输入0,1,2,3,4,此程序会输出相应索引元素的值。如果你输入一个超过数组长度的索引,如10,你会看到如下输出:
thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:19:19
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
该程序在索引操作中使用无效值时导致运行时错误。程序退出并显示错误消息,并且未执行最终的 println!陈述。当你尝试使用索引访问元素时,Rust 会检查你指定的索引是否小于数组长度。如果索引大于或等于长度,Rust 会恐慌。此检查必须在运行时进行,尤其是在这种情况下,因为编译器不可能知道用户稍后运行代码时将输入什么值。
这是 Rust 内存安全原则的一个例子。在许多低级语言中,不会执行此类检查,并且当您提供不正确的索引时,可以访问无效内存。Rust 通过立即退出而不是允许内存访问并继续来保护您免受此类错误的影响。第 9 章讨论了更多 Rust 的错误处理,以及如何编写既不会恐慌也不允许无效内存访问的可读、安全的代码。