📚 Rust 课程系列

  1. 课程概览
  2. 基础语法(一):变量、数据类型与字符串
  3. 切片(Slice):序列的借用视图(本文)
  4. 基础语法(二):运算符、表达式与控制流
  5. 函数与输入输出
  6. 所有权、借用与生命周期
  7. 结构体
  8. 枚举
  9. 模式匹配
  10. 类型系统:泛型、trait 与多态
  11. 集合与容器
  12. 错误处理与 Panic 恢复
  13. 模块、属性与宏
  14. 智能指针、迭代器与闭包
  15. 并发与异步编程
  16. Unsafe Rust 与常用 trait 详解
  17. 工具链、Cargo 与外部 crate
  18. 最佳实践、性能与调试

基础语法(一)中,数组、Vec 与字符串都是连续序列,却分属不同类型。Rust 用切片(slice)统一它们的访问接口–一种不拥有数据、仅记录起始指针与长度的借用视图。它是零成本抽象的典范:统一序列类型的访问接口,且无运行时开销。

胖指针:切片的内存本质

&[T]&str 在栈上都是胖指针(fat pointer),由数据指针与长度两个字段组成,因此比普通引用宽一倍:

1
2
3
4
5
6
use std::mem::size_of;

// 64 位平台
assert_eq!(size_of::<&[i32]>(), 16); // ptr + len
assert_eq!(size_of::<&str>(), 16); // ptr + len
assert_eq!(size_of::<&i32>(), 8); // 普通引用仅 ptr
graph LR
    subgraph s1 ["栈 胖指针"]
        P["ptr"]
        L["len"]
    end
    subgraph s2 ["连续数据"]
        D["[T] 序列"]
    end
    P -->|指向| D

💡 提示:胖指针多出的 8 字节长度字段是"携带边界信息"的代价,换来 O(1) 的长度查询与边界检查。String(24 字节)比 &str(16 字节)多一个 capacity 字段。

两种形态:&[T]&str

类型数据来源编码约束说明
&[T]数组、Vec、任意连续 T通用序列视图
&strString、字面量UTF-8&[u8] 的特化版

&str 本质是 &[u8] 加上"内部为合法 UTF-8"的保证,二者共享胖指针布局,诸多操作语义一致。&str 的字符串特性(String 互转、UTF-8 遍历、&mut str 等)详见《基础语法(一)》的字符串一节。

创建切片

1
2
3
4
5
6
7
8
9
10
11
12
13
let arr = [1, 2, 3, 4, 5];
let v = vec![10, 20, 30, 40, 50];

// Range 语法切取子序列
let a: &[i32] = &arr[1..4]; // [2, 3, 4]
let b: &[i32] = &v[2..]; // [30, 40, 50]
let c: &[i32] = &arr[..2]; // [1, 2]
let d: &[i32] = &arr[..]; // 全部
let e: &[i32] = &arr[..=1]; // [1, 2] 闭区间

// 数组、Vec 自动解引用为整体切片
let f: &[i32] = &arr; // Deref 强制转换
let g: &[i32] = &v;

Range 语法汇总(以 [1, 2, 3, 4, 5] 为例):

语法含义结果
a..b左闭右开&s[1..3] -> [2, 3]
a..从 a 到末尾&s[2..] -> [3, 4, 5]
..b从头到 b(不含)&s[..2] -> [1, 2]
..全部&s[..] -> 全部
..=b从头到 b(含)&s[..=1] -> [1, 2]

索引与越界安全

1
2
3
4
5
6
let s = [1, 2, 3, 4];

let x = s[0]; // 直接索引,越界 panic
let y = s.get(1); // Some(2)
let z = s.get(10); // None,不 panic
let m = s.get_mut(0); // Some(&mut 1)
方式越界行为返回类型
s[i]panicT / &T
s.get(i)返回 NoneOption<&T>
s.get_mut(i)返回 NoneOption<&mut T>

⚠️ 注意:字符串切片 &s[0..n]字节边界切取,若截断多字节 UTF-8 字符会 panic。安全做法是用 s.get(..n)(越界返回 None)或按 char_indices 计算字符边界,详见《基础语法(一)》的「字符串的遍历」一节。

遍历与常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
let s = [1, 2, 3, 4, 5];

// 三种迭代
for x in s.iter() {} // &i32
for x in s.iter_mut() {} // &mut i32
for x in s.into_iter() {} // i32,消费所有权

// 查询
s.contains(&3); // true
s.first(); // Some(&1)
s.last(); // Some(&5)
s.iter().position(|&x| x == 3); // Some(2)

// 切分
for c in s.chunks(2) {} // [1,2],[3,4],[5]
for w in s.windows(3) {} // [1,2,3],[2,3,4],...
let (l, r) = s.split_at(2); // [1,2],[3,4,5]

chunkswindows 的区别:

  • chunks(n)不重叠分块,每块长 n,最后一块可能不足 n
  • windows(n)滑动窗口,每窗长 n,步长 1,共 len - n + 1

可变切片 &mut [T]

可变切片允许原地修改元素,但不能改变长度–与 &mut str 的等长约束同理(详见《基础语法(一)》的「&mut str 详解」一节)。

1
2
3
4
5
6
7
8
9
10
11
let mut v = vec![3, 1, 4, 1, 5];
let s: &mut [i32] = &mut v;

s.sort(); // 原地排序
s.reverse(); // 反转
s.fill(0); // 全部置 0
s.swap(0, 1); // 交换两元素

// split_at_mut:同时获取两段不重叠可变引用
let (left, right) = s.split_at_mut(2);
// left: &mut [3,1] right: &mut [4,1,5]

💡 提示split_at_mut 是借用检查的精妙设计–编译器能证明两段不重叠,故可同时返回两个 &mut [T];手写等价逻辑会因"同一数据两个可变引用"被拒。

切片作为函数参数

函数参数优先用 &[T] 而非 &Vec<T>–切片更通用,可接收数组与 Vec 的引用,且不耦合具体容器类型:

1
2
3
4
5
6
7
8
9
fn sum(s: &[i32]) -> i32 {
s.iter().sum()
}

let arr = [1, 2, 3];
let v = vec![4, 5, 6];
sum(&arr); // ✅ 数组
sum(&v); // ✅ Vec
sum(&arr[1..]); // ✅ 子切片

💡 提示:空切片 &[] 合法(长度 0),可表示"无数据"而无需 Option&str 同理,空字符串 "" 是合法的零长度切片。&[T]&Vec<T>&str&String 的关系对比见《基础语法(一)》的「array vs &slice vs Vec」表。

切片模式匹配

切片支持在 match 中按结构模式匹配:

1
2
3
4
5
6
7
8
fn describe(s: &[i32]) -> &str {
match s {
[] => "空",
[x] => "单元素",
[a, b] => "两元素",
[first, ..] => "多元素", // .. 忽略中间
}
}

💡 提示[first, ..] 中的 .. 通配任意数量元素。切片模式是模式匹配的子集,完整语法见《模式匹配》

性能与零成本

  • 切片运算(取子切片、索引、len)均为 O(1)
  • 传递切片仅拷贝 16 字节胖指针,与序列长度无关
  • iter/chunks/windows 等方法零分配,仅产生迭代器
  • 切片是"视图",生命周期受限于所有者,完整推导见所有权、借用与生命周期

小结

  • 切片 &[T]/&str 是胖指针借用视图(ptr+len),统一数组、Vec、字符串的访问接口,无运行时开销
  • Range 语法(a..b/a../..b/../..=b)切取子序列;s[i] 越界 panic,get 返回 None
  • chunks 不重叠分块、windows 滑动窗口、split_at/split_at_mut 按位置切分
  • 可变切片 &mut [T] 可原地修改元素但不改长度;split_at_mut 借助借用检查同时返回两段不重叠可变引用
  • 函数参数优先用 &[T] 以获得通用性(接受数组、Vec 及其子切片)

下一篇基础语法(二):运算符、表达式与控制流将讲运算符一览、面向表达式的语法哲学与控制流构造。