Skip to content

切片基础

> 理解切片(&[T] 和 &str)的概念与语法,掌握范围操作符的各种写法。

为什么需要切片?

切片语法

概念名称: 切片是对集合中一段连续元素的引用。

语法结构:
┌──────────────────────────────────────┐
│  &集合[start..end]                   │
│        ↑    ↑    ↑                   │
│        引用 开始  结束(不含)         │
│                                       │
│  &s[0..5]   → 索引 0-4               │
│  &s[..5]    → 开头到 4               │
│  &s[5..]    → 5 到末尾               │
│  &s[..]     → 整个集合               │
│  &s[..=5]   → 0-5(包含结束)         │
└──────────────────────────────────────┘

最简示例

rust
fn main() {
    let s = String::from("hello world");
    let hello = &s[0..5];  // "hello"
    let world = &s[6..11]; // "world"
    println!("{} {}", hello, world);
}
▶ Run

问题场景

rust
// 问题:只想使用字符串的一部分
fn main() {
    let s = String::from("hello world");

    // 方案 1:创建新 String(低效)
    let hello = String::from("hello");  // 复制数据

    // 方案 2:使用切片(高效)
    let hello = &s[0..5];  // 只是引用,不复制
}
▶ Run

切片的优势

┌─────────────────────────────────────────────────────┐
│              切片 vs 复制                            │
├─────────────────────────────────────────────────────┤
│                                                     │
│  复制数据                                           │
│  ┌─────────────────┐                               │
│  │ 原始数据 (10MB) │                               │
│  │    ↓ 复制       │  ← 耗时、耗内存               │
│  │ 新数据 (10MB)   │                               │
│  └─────────────────┘                               │
│                                                     │
│  使用切片                                           │
│  ┌─────────────────┐                               │
│  │ 原始数据 (10MB) │                               │
│  │    ↑ 引用       │  ← 瞬间、零内存开销           │
│  │   切片 (16 字节) │     (指针 + 长度)             │
│  └─────────────────┘                               │
│                                                     │
└─────────────────────────────────────────────────────┘

什么是切片?

切片(Slice)= 对集合中一段连续元素的引用

┌────────────────────────────────────────┐
│          切片数据结构                   │
├────────────────────────────────────────┤
│  • 不拥有数据,只是引用                 │
│  • 可以指向数组、Vec、String 的一部分    │
│  • 大小在运行时确定(胖指针)           │
│  • 类型表示:&[T] 或 &str              │
└────────────────────────────────────────┘

字符串切片(&str)

基本语法

rust
fn main() {
    let s = String::from("hello world");

    // 创建字符串切片
    let hello = &s[0..5];   // "hello"
    let world = &s[6..11];  // "world"

    println!("{} {}", hello, world);

    // 验证类型
    println!("hello 类型:{:?}", std::any::type_name_of_val(&hello));
    // 输出:&str
}
▶ Run

范围语法详解

范围表示法:start..end

┌─────────────────────────────────────────────────────┐
│  语法        │  含义          │  示例 (s="hello")   │
├─────────────────────────────────────────────────────┤
│  start..end  │  start 到 end-1  │  s[0..3] = "hel"   │
│  start..     │  start 到末尾   │  s[2..] = "llo"    │
│  ..end       │  开头到 end-1   │  s[..3] = "hel"    │
│  ..          │  整个字符串     │  s[..] = "hello"   │
│  ..=end      │  开头到 end     │  s[..=2] = "hel"   │
└─────────────────────────────────────────────────────┘

重要:范围不包含 end 值(Rust 的 range 都是左闭右开)

语法糖(简写)

rust
fn main() {
    let s = String::from("hello world");

    // 以下写法等价

    // 完整写法
    let hello1 = &s[0..5];

    // 省略 0(从开头开始)
    let hello2 = &s[..5];

    // 验证相等
    assert_eq!(hello1, hello2);

    // 更多简写
    let world = &s[6..];     // 从 6 到末尾
    let all = &s[..];        // 整个字符串
    let empty = &s[5..5];    // 空字符串
}
▶ Run

内存布局详解

String 和切片的内存布局:

String s = "hello world"

┌─────────────────────────────────────────────────────┐
│                    栈(Stack)                       │
│                                                     │
│  ┌─────────────────────────────────────────┐       │
│  │              String s                    │       │
│  │  ┌───────┬───────┬───────┐              │       │
│  │  │  ptr  │  len  │  cap  │              │       │
│  │  │   ●───┼───11──┼───15──┤              │       │
│  │  └───┼───┴───────┴───────┘              │       │
│  │      │                                   │       │
│  │  ┌───┴─────────────────────────────┐    │       │
│  │  │         &str hello              │    │       │
│  │  │  ┌───────┬───────┐              │    │       │
│  │  │  │  ptr  │  len  │              │    │       │
│  │  │  │   ●───┼───5───┤              │    │       │
│  │  │  └───┼───┴───────┘              │    │       │
│  │  │      │                           │    │       │
│  │  └──────┼───────────────────────────┘    │       │
│  │         │                                 │       │
│  └─────────┼─────────────────────────────────┘       │
│            │                                          │
└────────────┼──────────────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│                    堆(Heap)                        │
│                                                     │
│  ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐    │
│  │ h │ e │ l │ l │ o │   │ w │ o │ r │ l │ d │    │
│  └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘    │
│   0   1   2   3   4   5   6   7   8   9  10  (索引) │
│   ↑                           ↑                     │
│   │                           │                     │
│   └─ s.ptr                    └─ s.ptr + 6         │
│   └─ hello.ptr                                        │
│                                                     │
└─────────────────────────────────────────────────────┘

关键点:
• String = (ptr, len, cap) = 24 字节
• &str = (ptr, len) = 16 字节
• 切片不拥有数据,只是指向现有数据的视图

字符串字面量的类型

&str 的本质

rust
fn main() {
    // 字符串字面量的类型是 &str
    let s: &str = "Hello, world!";

    // 更准确的类型是 &'static str
    // 'static 表示整个程序生命周期

    println!("类型:{:?}", std::any::type_name_of_val(&s));
    // 输出:&str
}
▶ Run

&'static str 详解

字符串字面量的存储:

┌─────────────────────────────────────────────────────┐
│              静态区(.rodata)                       │
│                                                     │
│  程序编译时,字符串字面量被放入静态区                │
│  整个程序运行期间都存在                            │
│                                                     │
│  ┌─────────────────────────────────────────┐       │
│  │  "Hello, world!\0"                      │       │
│  │  "Rust\0"                               │       │
│  │  "你好\0"                               │       │
│  │  ...                                    │       │
│  └─────────────────────────────────────────┘       │
│                                                     │
│  当写 let s = "Hello"; 时:                         │
│  • s 是一个 &str                                   │
│  • 指向静态区的 "Hello\0"                          │
│  • 生命周期是 'static                              │
│                                                     │
└─────────────────────────────────────────────────────┘

&str vs String 对比

rust
fn main() {
    // &str - 字符串切片
    let s1: &str = "hello";
    // • 不可变
    // • 通常指向静态区
    // • 大小:16 字节(指针 + 长度)
    // • 不能修改内容

    // String - 可增长字符串
    let mut s2: String = String::from("hello");
    // • 可变
    // • 数据在堆上
    // • 大小:24 字节(指针 + 长度 + 容量)
    // • 可以修改内容

    s2.push_str(" world");
    println!("{}", s2);
}
▶ Run

对比表格

┌─────────────────────────────────────────────────────┐
│              &str vs String 对比                    │
├──────────────┬──────────────────┬───────────────────┤
│    特性      │      &str        │     String        │
├──────────────┼──────────────────┼───────────────────┤
│ 所有权       │ 借用(引用)      │ 拥有              │
├──────────────┼──────────────────┼───────────────────┤
│ 可变性       │ 不可变           │ 可变              │
├──────────────┼──────────────────┼───────────────────┤
│ 数据存储     │ 引用其他数据      │ 堆上分配          │
├──────────────┼──────────────────┼───────────────────┤
│ 大小         │ 16 字节           │ 24 字节           │
├──────────────┼──────────────────┼───────────────────┤
│ 字面量       │ "hello"          │ String::from("hello") │
├──────────────┼──────────────────┼───────────────────┤
│ 修改内容     │ ❌ 不支持         │ ✅ push_str 等     │
├──────────────┼──────────────────┼───────────────────┤
│ 生命周期     │ 有生命周期参数    │ 拥有数据          │
└──────────────┴──────────────────┴───────────────────┘

小结

  • 切片是对集合中一段连续元素的引用,不拥有数据
  • &str 是字符串切片,&[T] 是数组切片
  • 范围语法:start..end(不含 end)、..start....end..=end
  • 切片是"胖指针":包含指针和长度,零拷贝高效

练习题

详见:练习题