数组与切片
数组是相同类型 T 的元素集合,存储在连续内存中。数组使用方括号 [] 创建;数组长度在编译期确定,并且长度属于类型签名的一部分:[T; 长度]。
切片和数组很相似,但切片长度编译期未知。
切片本质是一个占两个机器字的对象:第一个字是指向数据的指针,第二个字保存切片长度。(可以获取数组的一部分片段,所以叫切片)
机器字大小等同于 usize,由 CPU 架构决定(例如 x86-64 平台为 64 位)。
切片可以借用数组的一段区间,类型标记为 &[T]。
我们直接看例子,可能更具容易理解
use std::mem;
// 该函数接收切片借用
fn analyze_slice(slice: &[i32]) {
println!("切片第一个元素:{}", slice[0]);
println!("切片拥有 {} 个元素", slice.len());
}
fn main() {
// 定长数组(这里类型标注可以省略,编译器自动推导)定义了一个名叫xs的数组,里面的元素类型是i32,数组的元素个数是5,里面的元素分别是1 2 3 4 5
let xs: [i32; 5] = [1, 2, 3, 4, 5];
// 快速初始化:所有元素赋值为同一个值
// 创建一个长度为 500 的数组,数组里所有元素全部初始化为 0
// [初始值; 数组长度]
let ys: [i32; 500] = [0; 500];
// 数组下标从0开始
println!("数组首个元素:{}", xs[0]);
println!("数组第二个元素:{}", xs[1]);
// len() 返回数组元素总数
println!("数组元素数量:{}", xs.len());
// 数组分配在栈上
println!("数组占用字节数:{}", mem::size_of_val(&xs));
// 数组可以自动被借用转换成切片
println!("将整个数组借用为切片");
analyze_slice(&xs);
// 切片可以指向数组的某一段区间
// 语法形式 [起始索引..终止索引]
// 起始索引:包含在切片内
// 终止索引:不包含(是切片最后一位下标+1)
println!("截取数组一段作为切片");
analyze_slice(&ys[1 .. 4]);
// 空切片 &[] 示例
let empty_array: [u32; 0] = [];
assert_eq!(&empty_array, &[]);
assert_eq!(&empty_array, &[][..]); // 效果相同,写法更繁琐
// 使用 .get() 安全访问数组,返回 Option<T>
// 可以用match匹配,也可以搭配 .expect() 直接报错退出
for i in 0..xs.len() + 1 { // 故意多循环一次,触发越界场景
match xs.get(i) {
Some(xval) => println!("{}: {}", i, xval),
None => println!("越界啦!下标 {} 超出范围!", i),
}
}
// 常量下标越界访问数组 → 编译报错
//println!("{}", xs[5]);
// 切片常量下标越界 → 运行时panic
//println!("{}", xs[..][5]);
}Code language: Rust (rust)
运行结果
D:\rustdemo\cargodemo\hello_cargo>cargo run
Compiling hello_cargo v0.1.0 (D:\rustdemo\cargodemo\hello_cargo)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.59s
Running `target\debug\hello_cargo.exe`
数组首个元素:1
数组第二个元素:2
数组元素数量:5
数组占用字节数:20
将整个数组借用为切片
切片第一个元素:1
切片拥有 5 个元素
截取数组一段作为切片
切片第一个元素:0
切片拥有 3 个元素
0: 1
1: 2
2: 3
3: 4
4: 5
越界啦!下标 5 超出范围!Code language: CSS (css)
let ys: [i32; 500] = [0; 500];
[初始值; 数组长度]
第 1 部分 0:每个元素填充的值 分号 ;:固定分隔符 第 2 部分 500:数组长度
[0,0,0,0,…,0] // 一共写500个0
println!(“数组首个元素:{}”, xs[0]);
{} 就是占位符!Rust 的 println! 使用 {} 作为格式化占位符,默认不需要写序号。
例如:
println!("{} + {} = {}", 10, 20, 30);
// 输出:10 + 20 = 30Code language: JavaScript (javascript)
第 1 个{} → 第 1 个参数
第 2 个{} → 第 2 个参数
也可以数字索引模式(可以重复使用参数)
println!("{0} + {1} = {2}, {0}", 10, 20, 30);
// 输出:10 + 20 = 30, 10Code language: JavaScript (javascript)
甚至可以 命名参数(可读性更高)
println!("值:{v}", v=xs[0]);Code language: JavaScript (javascript)
还可以增加一些限定
let num = 3.14159;
println!("保留2位小数:{:.2}", num); // {:.2} 格式化规则Code language: JavaScript (javascript)
println!(“数组占用字节数:{}”, mem::size_of_val(&xs));
mem 是 std::mem 的简写:
完整路径:std::mem
属于 Rust 标准库,专门存放内存大小、类型转换、内存操作相关工具函数。
mem::size_of_val(变量引用)
计算「一个值实际占用多少字节」
Previous: Cargo
Next: 结构体 (struct)