bigywater.tech
Rust · 57

VecDeque 环形缓冲区

VecDeque 两端 O(1) 的秘密是环形缓冲区:头尾指针在固定缓冲区里绕圈,不搬数据。这篇用 as_slices 和元素地址直接看到「绕回」,并实测头部插入与 Vec 的差距,再看它为什么不能当切片用。

2 段
回绕后 as_slices 返回的两段切片
1006×+
5 万次头部插入,Vec::insert(0) 最快一次对 VecDeque 最慢一次的倍数下限
E0308
&VecDeque 当 &[T] 用

环形布局

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    with_capacity(8): len 0 cap 8
    push_back 1..=5:  [1, 2, 3, 4, 5]  as_slices = ([1, 2, 3, 4, 5], [])
    pop_front ×2:     [3, 4, 5]  as_slices = ([3, 4, 5], [])
    再 push_back 6..=9: [3, 4, 5, 6, 7, 8, 9]  as_slices = ([3, 4, 5, 6, 7, 8], [9])  (逻辑上连续,物理上绕回了缓冲区开头)
    push_front(0):    [0, 3, 4, 5, 6, 7, 8, 9]  as_slices = ([0, 3, 4, 5, 6, 7, 8], [9])  cap 8
    make_contiguous:  [0, 3, 4, 5, 6, 7, 8, 9]
    之后 as_slices = ([0, 3, 4, 5, 6, 7, 8, 9], [])

用地址证明回绕

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    cap 4
    逻辑下标 0 值  2  物理槽位 2
    逻辑下标 1 值  3  物理槽位 3
    逻辑下标 2 值 10  物理槽位 0
    逻辑下标 3 值 11  物理槽位 1

比 Vec 快多少

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    Vec::insert(0)     89.76 ms   VecDeque::push_front   0.074 ms
    Vec::insert(0)     74.46 ms   VecDeque::push_front   0.050 ms
    Vec::insert(0)     74.82 ms   VecDeque::push_front   0.052 ms

Vec 的扩容见Vec 扩容机制。这里的毫秒数是本机 release 模式的实测,绝对值因机器而异。

不是切片

实测 · 本机执行输出
  编译:成功
  运行(退出码 0):
    d = [3, 4, 5, 1, 2]  d[0] = 3  d[4] = 2  get(9) = None
  源码:
      1 | use std::collections::VecDeque;
      2 | fn main() {
      3 |     let d: VecDeque<i32> = (1..=5).collect();
      4 |     let sl: &[i32] = &d;
      5 |     println!("{:?}", sl);
      6 | }
  编译:失败
    error[E0308]: mismatched types
     --> <实验目录>/src/u57d2.rs:4:22
      |
    4 |     let sl: &[i32] = &d;
      |             ------   ^^ expected `&[i32]`, found `&VecDeque<i32>`
      |             |
      |             expected due to this
      |
      = note: expected reference `&[i32]`
                 found reference `&VecDeque<i32>`
    
    error: aborting due to 1 previous error
    
    For more information about this error, try `rustc --explain E0308`.
  编译:成功
  运行(退出码 0):
    make_contiguous().sort() 之后 [1, 2, 3, 4, 5]
    VecDeque → Vec: [1, 2, 3, 4, 5]
    Vec → VecDeque: [1, 2, 3, 4, 5] (as_slices ([1, 2, 3, 4, 5], []))

分步演示

VecDeque
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。耗时用 release + black_box,每组测 3 次
    • 没有验证:VecDeque 扩容时搬动数据的具体做法、其它容量下的回绕、迭代与 Vec 迭代的速度差、内部 head/len 字段(不是公开 API,只用 as_slices 和元素地址间接观察)
    京ICP备2026067502号-1京公网安备11010502063487号