基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

Rust 切片(Slice)在内存中是如何表示的?

知识点图片

在 Rust 中,切片(Slice,例如 &[T]&str)在内存中的表示被称为 “胖指针” (Fat Pointer)

与 C 语言中只包含内存地址的普通指针不同,Rust 的切片包含两个部分的数据。

内存结构

一个切片在底层实际上是一个结构体(虽然在语言层面是内置类型),它占用两个机器字长(Two machine words):

  1. 指针 (Pointer): 指向数据序列中第一个元素的内存地址。
  2. 长度 (Length): 切片中包含的元素个数(注意是元素的个数,不是字节数,除非元素类型是 u8)。

在 64 位系统上,一个切片占用 16 字节(8 字节指针 + 8 字节长度)。

图解表示

假设我们有一个 Vec<i32>,数据存储在堆上,然后我们创建了一个指向其中一部分数据的切片。

plaintext
let data = vec![10, 20, 30, 40, 50]; // data 在堆上
let slice = &data[1..4];             // slice 包含 [20, 30, 40]

内存布局如下:

plaintext
      栈 (Stack)                            堆 (Heap)
+-------------------+                 +-------------------+
|      slice        |                 |      data         |
+---------+---------+                 +-------------------+
|  ptr    |    ●----+-------------->  | 10 (index 0)      |
+---------+---------+                 +-------------------+
|  len    |    3    |                 | 20 (index 1) <---- start of slice
+---------+---------+                 +-------------------+
                                      | 30 (index 2)      |
                                      +-------------------+
                                      | 40 (index 3)      |
                                      +-------------------+
                                      | 50 (index 4)      |
                                      +-------------------+
  • ptr: 指向堆内存中 20 的地址(即切片的起始位置)。
  • len: 值为 3,表示该切片包含 3 个 i32 元素。

代码验证

我们可以通过 std::mem::size_of 来验证切片的大小是普通指针的两倍,也可以通过 unsafe 代码查看其原始部分。

plaintext
use std::mem;

fn main() {
    // 1. 验证大小
    let ptr_size = mem::size_of::<&i32>();      // 普通引用
    let slice_size = mem::size_of::<&[i32]>();  // 切片引用

    println!("普通指针大小: {} bytes", ptr_size); // 64位系统通常是 8
    println!("切片大小:    {} bytes", slice_size); // 64位系统通常是 16 (8 ptr + 8 len)

    // 2. 查看内部结构
    let arr = [1, 2, 3, 4, 5];
    let slice = &arr[1..3]; // 包含 [2, 3]

    // 将切片强转为原始部分 (ptr, len)
    // 在 Rust 内部,切片可以用 std::slice::from_raw_parts 重组
    let (ptr, len) = (slice.as_ptr(), slice.len());

    println!("切片指针地址: {:p}", ptr);
    println!("切片长度:    {}", len);
    println!("数组元素2地址: {:p}", &arr[1]); // 应该与切片指针地址相同
}

特殊情况:字符串切片 (&str)

字符串切片 &str 的内存表示与 &[u8] 完全相同,也是一个胖指针:

  1. ptr: 指向 UTF-8 字节序列的起始位置。
  2. len: 字节的长度(注意:这里是字节数,因为 str 底层是 u8,但必须保证是合法的 UTF-8 序列)。

总结与对比

类型 组成部分 说明
普通引用 (&T) ptr 只是一个内存地址。
切片 (&[T]) ptr + len 地址 + 元素个数。不拥有数据的所有权。
向量 (Vec<T>) ptr + len + cap 地址 + 元素个数 + 容量。拥有数据的所有权,可以增长。

核心理解: 切片是一个视图 (View)。它不拥有数据,只是通过 ptrlen 描述了“从哪里开始,到哪里结束”的一段连续内存。

00:00
00:00