数组 Array
数组是Rust内建的原始集合类型,它表示同类型、长度固定、有序的元素集合。
数组的类型为[T; N],其中T表示内部的类型,N表示数组的长度。
1.数组的定义
1.1 类型推导
若不给类型标注,rust也会自动推导数组内的类型及长度
let arr=[1,2,3,4,5];1.2 类型标注
数组的类型标注使用[T; N]语法标注。其中T表示类型,N表示值的数量,数组值的定义有两种
1.第一种形是确定所有元素的值,每个值都必须是相同类型的元素,而且必须有。
let arr: [T; N] = [val1, val2, val3...];
// 例子
let arr: [u32; 5] = [1,2,3,4,5]第二种形式把所有元素都初始化为同一个值val,比如let arr = ["hello", 100]就是定义了一个一百个hello的数组。
let arr: [T; N] = [val; N];
// 例子
let arr = ["hello", 100]1.3 数组长度注意事项
Rust的原生数组通常定义在栈上,基于T和N在编译期就可以确定内存占用情况。比如说[i32; 10]类型的数组,每个i32大小为4 byte,那么整个数组大小就是40 byte。为了保证其编译期可以确定大小的特性,Rust要求N必须在编译期可以求值。
例如:
const fn const_func() -> usize {
10
}
const LEN: usize = 10;
let arr = [2025; 10];
let arr = [2025; LEN];
let arr = [2025; const_func()];以上三个定义都是合法的,这里展示了三种最常见的编译期可以确定值的表达式:字面量、const 常量、CFTE 函数。
但是比如说使用变量就是非法的:
let len: usize = 10;
let arr = [202; len];此处的len是一个变量,用它初始化arr编译无法通过,因为编译期无法确定数组长度。
2.数组元素的访问
与元组一样,数组也通过下标访问,从0开始。不过数组需要使用[]
let val = arr[index];其中index是要访问的下标,比如:
let mut arr = [2025; 10];
arr[1] = 2026
let val = arr[1];可以把arr[pos]放在等号左边,对指定元素进行赋值,当然前提是mut。也可以用其它变量接收指定下标的值。
Rust 的数组访问有严格的边界检查,编译时能发现的越界会直接报错,运行时越界会导致 panic。
Rust对安全要求极高,对数组也是一样。之前说过,数组长度N是在编译期就可以确定的。如果你在pos位置传入的表达式也是编译期可以确定的,那么Rust在编译期就可以进行越界检查,防止访问未开辟的内存。
比如:
let arr = [2025; 10];
let val = arr[10]; // 编译失败由于arr的下标范围是0 ~ 9,arr[10]在编译期直接失败,减少了运行时错误。
当然这也不是万能的,比如说pos位置传入了一个非编译期求值的表达式:
let arr = [2025; 10];
let num = 10;
let val = arr[num]; // 编译成功,运行 panic虽然num已经越界了,但是编译期无法确定num的值,只能等到运行时触发panic。
2.1 Index/IndexMut
上面通过arr[index]访问数组元素的核心机制是:IndexTrait
当你写下 arr[i]时,Rust 编译器实际上在后台调用了 IndexTrait 的 index方法。
container[index]实际上是*container.index(index)的语法糖container[index]实际上是*container.index_mut(index)的语法糖
对于数组 [T; N],标准库中大致有这样的实现:
impl<T, const N: usize> Index<usize> for [T; N] {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
// 内部会进行边界检查
// 如果 index >= N,则会 panic
&self[index] // 伪代码,实际实现更复杂
}
}所以:
let arr = [10, 20, 30];
let x = arr[1];
// 编译器将其转换为:
let x = *arr.index(1); // index 返回 &T,* 解引用拿到 T当我们给一个数组设置为mut,然后通过下标访问时,就是IndexMutTrait在发力
let mut arr = [1, 2, 3, 4, 5];
// 使用 IndexMut 修改元素
arr[0] = 10; // 等价于 *arr.index_mut(0) = 102.2 Range语法
使用Struct std::ops::Range中的Range语法可以从数组中提取连续的一段元素作为切片
Range的结构:
pub struct Range<Idx> {
pub start: Idx,
pub end: Idx,
}里面只有两个元素,访问的区间为[start, end),左包含,右不包含
- start:开始的下标
- end:结束的下标
Range语法
start..end使用该语法访问数组,返回的不是单个元素,而是一个切片 &[T],这是因为数组(和切片)实现了 Index<Range<usize>>。
fn main() {
let arr = [1, 2, 3, 4, 5];
let part = &arr[1..3]; // part 的类型是 &[i32]
println!("{:#?}", part); // [2,3]
}Range 的本质是结构体,Range语法编译期会被编译成Range结构体
let r = 1..5; // 语法糖
// 等价于:
let r_explicit = Range { start: 1, end: 5 };Range只是Range结构体家族中的其中一个,其他还有
| 语法 | 对应的 Struct | 含义 |
|---|---|---|
a..b | Range | [a, b)左闭右开 |
a..=b | RangeInclusive | [a, b]全闭区间 |
a.. | RangeFrom | [a, ∞) |
..b | RangeTo | (-∞, b) |
..=b | RangeToInclusive | (-∞, b] |
.. | RangeFull | (-∞, ∞)整个范围 |
3.数组的遍历
数组的遍历通常使用for循环
for in遍历
这是rust中最简单的数组遍历,但是遍历时不可修改元素,且获取所有权,遍历后不可再使用元素
let arr = [1, 2, 3, 4, 5];
for item in arr {
println!("{}", item);
}不可变借用遍历
通过对数组的引用来遍历,安全读取,不转移所有权
let arr = [1, 2, 3, 4, 5];
for item in &arr {
println!("{}", item);
}可变借用遍历
需要使用mut定义数组,并且遍历时也要使用&mut,不转移所有权
let mut arr = [1, 2, 3, 4, 5];
for item in &mut arr {
*item += 10; // 解引用
println!("{}", item);
}iter()遍历
数组有一个iter()方法,返回一个迭代器,逐个返回每个元素的不可变引用。不转移所有权
let arr = [1, 2, 3, 4, 5];
for item in arr.iter() {
println!("{}", item);
}into_iter()遍历
into_iter()会消耗掉原数组,获取其所有权。遍历后,原数组将无法再被使用。这通常在你确定之后不再需要该数组时使用
let arr = [1, 2, 3, 4, 5];
for item in arr.into_iter() {
println!("{}", item);
}iter_mut()遍历
数组的iter_mut()方法创建一个可变引用的迭代器,同样需要使用mut定义数组,不转移所有权
let mut arr = [1, 2, 3, 4, 5];
for item in arr.iter_mut() {
*item += 10;
println!("{}", item);
}Range遍历
数组的len()方法返回数组的长度,通过Range可以让我们使用下标访问数组元素,并且也可以修改
若需要修改数组元素,记得使用mut定义数组
这时候循环遍历是数组的下表,而不是当前索引值
fn main() {
let mut arr = [1, 2, 3, 4, 5];
for index in 0..arr.len() {
arr[index] += 10;
println!("{}", arr[index]);
}
}enumerate()遍历
enumerate()方法可以同时使用索引和值,不获取所有权,不可修改元素
let arr = ["Alice", "Bob", "Charlie"];
for (index, value) in arr.iter().enumerate() {
println!("第{}个元素是: {}", index + 1, value);
}