Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

切片

Rust 中的 slice(切片)是一种引用集合中连续元素的视图,而不拥有这些元素。它类似于数组或向量的子视图,使用 &[T] 表示不可变切片,&mut [T] 表示可变切片。Slice 是借用的一部分,遵守借用规则,确保内存安全。Slice 常用于字符串、数组和向量,帮助避免不必要的拷贝,提高效率。

1. Slice 简介

  • 什么是 slice? :Slice 是对数据序列的引用视图,指向连续内存块。不拥有数据,只借用。长度在运行时确定。
  • 语法&[T](不可变)、&mut [T](可变)。T 是元素类型。
  • 优势 :零拷贝访问子集;函数参数通用(如接受 &[i32] 而非 Vec 或 [i32; N])。
  • 与数组/向量的关系 :数组是固定大小,向量是动态。Slice 可以从两者创建。
  • 字符串 slice&str&[u8] 的特殊形式,处理 UTF-8。
fn main() {
    let s = String::from("hello world");
    let hello = &s[0..5];
    let world = &s[6..11];
    println!("{}", hello);  // 输出: hello
    println!("{}", world);  // 输出: world
}
  • 解释[start..end] 是半开区间(包括 start,不包括 end)。&arr[..] 是全切片。Slice 借用 arr,借用规则适用。

对于字符串而言,切片就是对 String 类型中某一部分的引用,它看起来像这样:

切片

胖指针(Fat Pointer)

  • 数据结构:对比普通引用(1个字长指针)与切片引用(2个字长)。
  • 组成部分:
    • Pointer:指向数据的起始位置。
    • Length:切片包含的元素个数。
  • 内存视图:在栈上存储元数据,在堆/静态区查看数据。

2. 创建 Slice

Slice 通过借用和范围运算符创建。

  • 范围语法
    • [start..end]:从 start 到 end-1。
    • [..end]:从 0 到 end-1。
    • [start..]:从 start 到结束。
    • [..] :整个集合。
  • 从向量/数组 :直接 &vec[start..end]。
  • 边界检查 :运行时检查,如果越界 panic!(安全)。

示例:各种创建方式

fn main() {
    let vec = vec![10, 20, 30, 40, 50];
  
    let full = &vec[..];      // 全切片: [10, 20, 30, 40, 50]
    let first_three = &vec[0..3];  // [10, 20, 30]
    let last_two = &vec[3..];     // [40, 50]
  
    println!("{:?}", first_three);
}
  • 解释 :Vec 和数组都支持。Slice 的 len() 返回元素数,get(i) 返回 Option<&T>(安全访问)。

可变 slice

fn main() {
    let mut vec = vec![1, 2, 3];
    let slice = &mut vec[1..3];  // 可变借用
  
    slice[0] = 20;  // 修改 vec[1]
    println!("{:?}", vec);  // 输出: [1, 20, 3]
}
  • 解释 :可变 slice 允许修改元素,但遵守独占借用规则。

3. 字符串 Slice (&str)

字符串 slice 是常见的,处理 String 或 str。

示例:字符串 slice

fn first_word(s: &str) -> &str {  // 既能接受 String 的切片,也能接受字符串字面量
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}

fn main() {
    let s = String::from("hello world");
    let word = first_word(&s);  // &String 隐式转为 &str
    
    println!("{}", word);  // 输出: hello
    
    // s.clear();  // 错误!word 借用期间不能修改 s
}
  • 解释&str 是 UTF-8 安全的。as_bytes() 转为 &[u8]。切片索引必须在字符边界(否则 panic!)。用 chars() 或 bytes() 迭代以避免。

4. 多维 Slice

Slice 可以是多维的,如 &[[T]]

示例:矩阵 slice

fn main() {
    let matrix = vec![vec![1, 2], vec![3, 4]];
    let row = &matrix[0][..];  // &[i32]: [1, 2]
    
    println!("{:?}", row);
}
  • 解释:嵌套借用。复杂时考虑扁平化或专用 crate。

注意事项: 索引越界:如果你请求的范围超出了集合边界(如 &s[0..100]),Rust 会在运行时 panic。 UTF-8 字符:对于字符串切片,索引必须落在字符边界上。如果在多字节字符(如中文)中间切片,程序会崩溃。