数组基础
> 数组是固定长度的同类型元素序列,存储在栈上。掌握数组的创建、访问和切片操作。
为什么需要集合类型?
数组语法
概念名称: 数组是固定长度的同类型元素序列,存储在栈上。
语法结构:
┌──────────────────────────────────────┐
│ let 变量: [类型; 长度] = [值1, 值2, ...];│
│ ↑ ↑ ↑ ↑ │
│ 名字 元素类型 元素数量 初始值 │
│ │
│ let arr = [1, 2, 3]; │
│ let arr: [i32; 3] = [1, 2, 3]; │
│ let zeros = [0; 10]; → 10个0 │
│ │
│ 访问:arr[0], arr[1], arr.len() │
└──────────────────────────────────────┘最简示例
rust
▶ Runfn main() {
let arr = [1, 2, 3, 4, 5];
println!("第一个:{}", arr[0]);
println!("长度:{}", arr.len());
}问题场景
rust
▶ Run// 问题:存储多个学生的分数
// 方案 1:使用独立变量(糟糕)
let score1 = 90;
let score2 = 85;
let score3 = 78;
let score4 = 92;
// ...如果有 100 个学生怎么办?
// 方案 2:使用数组或 Vec(优秀)
let scores = [90, 85, 78, 92]; // 固定数量
let mut scores_vec = vec![90, 85, 78, 92]; // 可增长数组与 Vec 的对比
┌─────────────────────────────────────────────────────┐
│ 数组 vs Vec 对比 │
├─────────────────────────────────────────────────────┤
│ │
│ 数组 (Array) │
│ ├── 固定长度(编译时确定) │
│ ├── 存储在栈上(通常更快) │
│ ├── 类型:[T; N](T 是元素类型,N 是长度) │
│ └── 适用:元素数量固定的场景 │
│ │
│ Vec(可增长向量) │
│ ├── 可变长度(运行时动态调整) │
│ ├── 数据存储在堆上 │
│ ├── 类型:Vec<T> │
│ └── 适用:元素数量变化的场景 │
│ │
│ 共同点: │
│ • 所有元素必须是相同类型 │
│ • 索引从 0 开始 │
│ • 提供迭代器支持 │
│ • 边界检查(安全) │
│ │
└─────────────────────────────────────────────────────┘数组(Array)详解
创建数组的五种方式
rust
▶ Runfn main() {
// 方式 1:直接列出所有元素
let numbers = [1, 2, 3, 4, 5];
// 方式 2:显式类型标注
let explicit: [i32; 5] = [1, 2, 3, 4, 5];
// 方式 3:重复元素(非常实用)
let zeros = [0; 10]; // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
let ones = [1; 5]; // [1, 1, 1, 1, 1]
// 方式 4:空数组
let empty: [i32; 0] = [];
let empty2: [bool; 0] = [];
// 方式 5:从表达式创建
let computed = [1 + 2, 3 * 4, 5]; // [3, 12, 5]
println!("numbers: {:?}", numbers);
println!("zeros: {:?}", zeros);
println!("empty: {:?}", empty);
}数组的内存布局
let arr = [10, 20, 30, 40, 50];
内存布局(连续存储):
栈(Stack)
┌─────┬─────┬─────┬─────┬─────┐
│ 10 │ 20 │ 30 │ 40 │ 50 │
└─────┴─────┴─────┴─────┴─────┘
↑ ↑ ↑ ↑ ↑
0 1 2 3 4 ← 索引
特点:
• 元素连续存储,访问高效
• 通过索引直接计算偏移量:base + index * sizeof(T)
• 缓存友好,遍历时预取效果好访问数组元素
rust
▶ Runfn main() {
let numbers = [10, 20, 30, 40, 50];
// 方式 1:索引访问(使用 [])
let first = numbers[0]; // 10
let third = numbers[2]; // 30
let last = numbers[4]; // 50
// 动态索引
let index = 3;
let value = numbers[index]; // 40
// 反向索引(Rust 1.77+)
let last = numbers[5..][0]; // 50(切片后取第一个)
println!("first: {}, third: {}, last: {}", first, third, last);
// 方式 2:安全访问(使用 get 方法)
match numbers.get(2) {
Some(value) => println!("索引 2 的值:{}", value),
None => println!("索引超出范围"),
}
// 越界访问会返回 None,不会 panic
match numbers.get(10) {
Some(value) => println!("{}", value),
None => println!("索引 10 超出范围!"), // 会执行这里
}
}数组越界的危险
rust
▶ Runfn main() {
let numbers = [1, 2, 3];
// ❌ 危险:直接索引越界会 panic
// let value = numbers[10]; // 程序崩溃!
// ✅ 安全:使用 get 方法
if let Some(value) = numbers.get(10) {
println!("{}", value);
} else {
println!("安全处理越界情况");
}
}
// panic 输出:
// thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 10'数组常用方法
rust
▶ Runfn main() {
let numbers = [1, 2, 3, 4, 5];
// 获取长度
println!("长度:{}", numbers.len()); // 5
// 检查是否为空
println!("是否为空:{}", numbers.is_empty()); // false
// 检查是否包含某元素
println!("包含 3: {}", numbers.contains(&3)); // true
println!("包含 10: {}", numbers.contains(&10)); // false
// 获取首尾元素(返回 Option)
println!("第一个:{:?}", numbers.first()); // Some(1)
println!("最后一个:{:?}", numbers.last()); // Some(5)
let empty: [i32; 0] = [];
println!("空数组的第一个:{:?}", empty.first()); // None
}数组切片(Slice)
rust
▶ Runfn main() {
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// 创建切片(借用部分数组)
let slice: &[i32] = &numbers[2..5]; // [3, 4, 5]
println!("原数组:{:?}", numbers);
println!("切片 [2..5]: {:?}", slice);
// 切片的各种形式
let all = &numbers[..]; // 整个数组 [1..10]
let first_three = &numbers[..3]; // [1, 2, 3]
let last_three = &numbers[7..]; // [8, 9, 10]
let single = &numbers[4..5]; // [5](切片,不是值)
println!("全部:{:?}", all);
println!("前三个:{:?}", first_three);
println!("后三个:{:?}", last_three);
// 切片的长度
println!("切片长度:{}", slice.len()); // 3
}数组迭代
rust
▶ Runfn main() {
let numbers = [10, 20, 30, 40, 50];
// 方式 1:for in 迭代(借用)
println!("迭代(借用):");
for num in &numbers {
println!("{}", num);
}
// numbers 仍然可用
println!("原数组:{:?}", numbers);
// 方式 2:迭代并修改
let mut mutable = [1, 2, 3, 4, 5];
for num in &mut mutable {
*num *= 2;
}
println!("翻倍后:{:?}", mutable); // [2, 4, 6, 8, 10]
// 方式 3:带索引迭代
for (index, value) in numbers.iter().enumerate() {
println!("numbers[{}] = {}", index, value);
}
// 方式 4:获取所有权(适用于可 Copy 类型)
for num in numbers {
println!("{}", num);
}
}小结
- 数组
[T; N]:固定长度,栈上存储,类型包含长度信息 - 创建方式:
[1, 2, 3]、[0; 10](重复元素)、显式类型标注 - 访问:索引
arr[i](越界 panic)、arr.get(i)(返回 Option) - 切片
&[T]:借用部分数组,是数组和 Vec 的通用接口
练习题
详见:练习题