Skip to content

数组 Array

数组是Rust内建的原始集合类型,它表示同类型、长度固定、有序的元素集合

数组的类型为[T; N],其中T表示内部的类型,N表示数组的长度。

1.数组的定义

1.1 类型推导

若不给类型标注,rust也会自动推导数组内的类型及长度

rust
let arr=[1,2,3,4,5];

1.2 类型标注

数组的类型标注使用[T; N]语法标注。其中T表示类型,N表示值的数量,数组值的定义有两种

1.第一种形是确定所有元素的值,每个值都必须是相同类型的元素,而且必须有。

rust
let arr: [T; N] = [val1, val2, val3...];

// 例子
let arr: [u32; 5] = [1,2,3,4,5]

第二种形式把所有元素都初始化为同一个值val,比如let arr = ["hello", 100]就是定义了一个一百个hello的数组。

rust
let arr: [T; N] = [val; N];  

// 例子
let arr = ["hello", 100]

1.3 数组长度注意事项

Rust的原生数组通常定义在上,基于TN在编译期就可以确定内存占用情况。比如说[i32; 10]类型的数组,每个i32大小为4 byte,那么整个数组大小就是40 byte。为了保证其编译期可以确定大小的特性,Rust要求N必须在编译期可以求值

例如:

rust
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 函数

但是比如说使用变量就是非法的:

rust
let len: usize = 10;
let arr = [202; len];

此处的len是一个变量,用它初始化arr编译无法通过,因为编译期无法确定数组长度。

2.数组元素的访问

与元组一样,数组也通过下标访问,从0开始。不过数组需要使用[]

rust
let val = arr[index];

其中index是要访问的下标,比如:

rust
let mut arr = [2025; 10];
arr[1] = 2026
let val = arr[1];

可以把arr[pos]放在等号左边,对指定元素进行赋值,当然前提是mut。也可以用其它变量接收指定下标的值。

Rust 的数组访问有严格的边界检查,编译时能发现的越界会直接报错,运行时越界会导致 panic

Rust对安全要求极高,对数组也是一样。之前说过,数组长度N是在编译期就可以确定的。如果你在pos位置传入的表达式也是编译期可以确定的,那么Rust在编译期就可以进行越界检查,防止访问未开辟的内存。

比如:

rust
let arr = [2025; 10];
let val = arr[10]; // 编译失败

由于arr的下标范围是0 ~ 9arr[10]在编译期直接失败,减少了运行时错误。

当然这也不是万能的,比如说pos位置传入了一个非编译期求值的表达式:

rust
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],标准库中大致有这样的实现:

rust
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] // 伪代码,实际实现更复杂
    }
}

所以:

rust
let arr = [10, 20, 30];
let x = arr[1];
// 编译器将其转换为:
let x = *arr.index(1); // index 返回 &T,* 解引用拿到 T

当我们给一个数组设置为mut,然后通过下标访问时,就是IndexMutTrait在发力

rust
let mut arr = [1, 2, 3, 4, 5];

// 使用 IndexMut 修改元素
arr[0] = 10;           // 等价于 *arr.index_mut(0) = 10

2.2 Range语法

使用Struct std::ops::Range中的Range语法可以从数组中提取连续的一段元素作为切片

Range的结构:

rust
pub struct Range<Idx> {
    pub start: Idx,
    pub end: Idx,
}

里面只有两个元素,访问的区间为[start, end)左包含,右不包含

  • start:开始的下标
  • end:结束的下标

Range语法

rust
start..end

使用该语法访问数组,返回的不是单个元素,而是一个切片 &[T],这是因为数组(和切片)实现了 Index<Range<usize>>

rust
fn main() {
    let arr = [1, 2, 3, 4, 5];
    let part = &arr[1..3]; // part 的类型是 &[i32]

    println!("{:#?}", part); // [2,3]
}

Range 的本质是结构体,Range语法编译期会被编译成Range结构体

rust
let r = 1..5; // 语法糖
// 等价于:
let r_explicit = Range { start: 1, end: 5 };

Range只是Range结构体家族中的其中一个,其他还有

语法对应的 Struct含义
a..bRange[a, b)左闭右开
a..=bRangeInclusive[a, b]全闭区间
a..RangeFrom[a, ∞)
..bRangeTo(-∞, b)
..=bRangeToInclusive(-∞, b]
..RangeFull(-∞, ∞)整个范围

3.数组的遍历

数组的遍历通常使用for循环

for in遍历

这是rust中最简单的数组遍历,但是遍历时不可修改元素,且获取所有权,遍历后不可再使用元素

rust
let arr = [1, 2, 3, 4, 5];

for item in arr {
    println!("{}", item);
}

不可变借用遍历

通过对数组的引用来遍历,安全读取,不转移所有权

rust
let arr = [1, 2, 3, 4, 5];

for item in &arr {
    println!("{}", item);
}

可变借用遍历

需要使用mut定义数组,并且遍历时也要使用&mut,不转移所有权

rust
let mut arr = [1, 2, 3, 4, 5];

for item in &mut arr {
    *item += 10;   // 解引用
    println!("{}", item);
}

iter()遍历

数组有一个iter()方法,返回一个迭代器,逐个返回每个元素的不可变引用。不转移所有权

rust
let arr = [1, 2, 3, 4, 5];

for item in arr.iter() {
    println!("{}", item);
}

into_iter()遍历

into_iter()会消耗掉原数组,获取其所有权。遍历后,原数组将无法再被使用。这通常在你确定之后不再需要该数组时使用

rust
let arr = [1, 2, 3, 4, 5];

for item in arr.into_iter() {
    println!("{}", item);
}

iter_mut()遍历

数组的iter_mut()方法创建一个可变引用的迭代器,同样需要使用mut定义数组,不转移所有权

rust
let mut arr = [1, 2, 3, 4, 5];

for item in arr.iter_mut() {
    *item += 10;
    println!("{}", item);
}

Range遍历

数组的len()方法返回数组的长度,通过Range可以让我们使用下标访问数组元素,并且也可以修改

若需要修改数组元素,记得使用mut定义数组

这时候循环遍历是数组的下表,而不是当前索引值

rust
fn main() {
    let mut arr = [1, 2, 3, 4, 5];

    for index in 0..arr.len() {
        arr[index] += 10;
        println!("{}", arr[index]);
    }
}

enumerate()遍历

enumerate()方法可以同时使用索引和值,不获取所有权,不可修改元素

rust
let arr = ["Alice", "Bob", "Charlie"];
for (index, value) in arr.iter().enumerate() {
    println!("第{}个元素是: {}", index + 1, value);
}

4.数组的方法

MIT Licensed