数组 (Array) 与切片 (Slice)

数组与切片

数组是相同类型 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));

memstd::mem 的简写:

完整路径:std::mem

属于 Rust 标准库,专门存放内存大小、类型转换、内存操作相关工具函数。

mem::size_of_val(变量引用)

计算「一个值实际占用多少字节」

Previous:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注