Rust in Everything
基于 bytes 1.12.1

bytes 源码解析

全卷只对着一个版本写:

crate版本tagcommit日期
bytes1.12.1v1.12.176c0fbb2026-07-08

bytes 是 tokio 组织里最底层的库之一,默认 feature 下没有任何依赖。前几卷已经多次用到它:hyper 把 socket 读进一个 BytesMut(读写缓冲),请求头和 body 分块切出来都是 Bytes(解析与编码、Incoming 与 body 通道),axum 的 Bytes 提取器把整个 body 收成一个 Bytes(body 提取器)。那几页讲「谁在用」,这一卷讲「它怎么做到不拷贝」。

bytes 的核心是两个类型,全卷沿它们的两条线展开:

  • 共享主线(Bytes,只读):一个 Bytes 是四个字:ptr、len、data 和一个 &'static Vtable。clone、drop、转回 Vec / BytesMut 都经由 vtable 分派,所以同一个类型背后可以是 'static 切片、Vec 转来的缓冲、引用计数的共享块、任意拥有者,或者 BytesMut 冻结过来的块。slice、split_to、split_off 只改 ptr 和 len,再 clone 一次。
  • 构建主线(BytesMut,可写,唯一拥有):BytesMut 用 data 字段的最低位区分两种形态:还是一个 Vec(KIND_VEC),或者已经和别的句柄共享一块分配(KIND_ARC)。写入走 BufMut;split_to 把前面一段切走,第一次切时从 KIND_VEC 提升成 KIND_ARC;freeze 把它变成 Bytes;reserve 在原地回收、整块复用和重新分配之间选一个。

hyper 读一个请求正好把两条线走一遍:socket 读进 BytesMut,split_to 切出请求头,freeze 成 Bytes,再用 slice 取出 URI 和每个头部的值;BytesMut 留下的部分接着读下一段。

flowchart TB
  intro[00 导读] --> overview[01 全景]
  overview --> bytes[02 Bytes]
  bytes --> bmut[03 BytesMut]
  bmut --> buf[04 Buf 与 BufMut]
  buf --> design[05 设计取舍]
  design --> pit[06 常见坑]
  pit --> site[07 在本站中]
  site --> hands[08 动手]

各部分

  1. 00 导读:锁定的版本和 1.12.0 到 1.12.1 的差异,两条主线,阅读路线;src/ 各文件、feature、测试与 CI 脚本。
  2. 01 全景:bytes 解决什么问题;一块缓冲从分配到释放的一生。
  3. 02 Bytes:本卷重点。结构与 vtable、几种存储、clone 与提升、切片。
  4. 03 BytesMut:data 字段的位编码、split 与 freeze、reserve。
  5. 04 Buf 与 BufMut:读写游标的两个 trait、UninitSlice、Chain / Take / Limit 等适配器。
  6. 05 设计取舍:把各章的取舍汇总到一起。
  7. 06 常见坑:用 panic 原文或现象做标题,每个都先跑出最小复现。
  8. 07 在本站中:本站(开源)代码里哪里直接或间接用到 bytes;本站锁的 1.12.0 与本卷的差异。
  9. 08 动手:不用 bytes,从零写一个带手写 vtable 的最小 Bytes 和只有两种形态的迷你 BytesMut。

从「导读」开始。只想会用 bytes 的读者,读 01 全景、02 的前两章、04 和常见坑;想读懂实现的,按顺序读,02 是全卷的重点:两条主线最后都落到 Bytes 的 vtable 上。

💬 关联讨论

发起讨论
加载中...