Skip to content

Slice 切片

本质是引用

  • 不持有所有权的一种数据类型
  • 指向字符串/数组等连续内存类型中一部分数据的引用

切片语法

语法数学表示包含终点吗?例子 (1..3)
start..end[start, end)不包含1, 2
start..=end[start, end]包含1, 2, 3
rust
let s = String::from("hello world");
let hello = &s[..5];        // [..5] = [0..5]
let world = &s[6..];        // [6..] = [6..s.len()]
let hello_world = &s[..];   // [..] = [0..s.len()]

胖指针(Fat Pointer)

切片在内存中被表示为胖指针,由两部分组成:

  • 指针: 指向数据起始元素的内存地址
  • 长度: 记录切片包含的元素个数

这使得切片高效且轻量,只是对现有数据的"窗口"视图,无需复制底层数据.

只有指针就是普通指针(瘦指针),指针 + 长度才是胖指针,切片类型总是胖指针

可切片的数据类型

只要数据在内存中连续存储,就可以创建切片:

  • 数组: [i32; 5] → 切片类型 &[i32]
  • 向量: Vec<T> → 切片类型 &[T]
  • 字符串: String&str → 切片类型 &str

&str 与切片的关系

&str 本质上是字符串切片:

  • 定义: 对 String 或字符串字面量中 UTF-8 字节序列的引用
  • 推荐用法: 函数参数优先使用 &str 而非 &String,因为 &str 能接收 &String、字符串字面量和本地切片
rust
fn print_message(msg: &str) {
   println!("{}", msg);
}

let s = String::from("hello");
print_message(&s);      // &String 自动强制转换为 &str
print_message("world"); // 字符串字面量直接作为 &str

为什么切片只能通过引用

切片类型([T]str)是 DST(Dynamic Sized Type):

  • 编译时大小未知,无法直接存储在栈上
  • 只能通过引用使用,使其成为固定大小的胖指针
  • &[T]&str 总是固定 16 字节(指针 8 字节 + 长度 8 字节)
rust
// ✗ 错误: DST 无法直接存储
let slice: [i32] = [1, 2, 3][..];

// ✓ 正确: 通过引用使用
let slice: &[i32] = &[1, 2, 3][..];

UTF-8 字节边界

对字符串切片时,索引必须落在合法的 UTF-8 字符边界上,否则会发生 panic:

rust
let s = "你好world";
// ✗ 错误: 在多字节字符中间切片
// let word = &s[0..2];

// ✓ 正确: 在字符边界处切片
let word = &s[0..3];   // "你" 是 3 字节

// ✓ 推荐: 使用迭代器避免索引错误
let chars: String = s.chars().take(1).collect();

本质是借用

  • 切片是一种借用行为,不获取所有权
  • 对应所有权规则中的"引用"
  • 受借用规则约束(同作用域同变量只能有一个可变引用或多个不可变引用)

基于 MIT 协议发布