bytes 源码解析
全卷只对着一个版本写:
| crate | 版本 | tag | commit | 日期 |
|---|---|---|---|---|
| bytes | 1.12.1 | v1.12.1 | 76c0fbb | 2026-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 动手]
各部分
- 00 导读:锁定的版本和 1.12.0 到 1.12.1 的差异,两条主线,阅读路线;
src/各文件、feature、测试与 CI 脚本。 - 01 全景:bytes 解决什么问题;一块缓冲从分配到释放的一生。
- 02 Bytes:本卷重点。结构与 vtable、几种存储、clone 与提升、切片。
- 03 BytesMut:
data字段的位编码、split 与 freeze、reserve。 - 04 Buf 与 BufMut:读写游标的两个 trait、
UninitSlice、Chain/Take/Limit等适配器。 - 05 设计取舍:把各章的取舍汇总到一起。
- 06 常见坑:用 panic 原文或现象做标题,每个都先跑出最小复现。
- 07 在本站中:本站(开源)代码里哪里直接或间接用到 bytes;本站锁的 1.12.0 与本卷的差异。
- 08 动手:不用 bytes,从零写一个带手写 vtable 的最小
Bytes和只有两种形态的迷你BytesMut。
从「导读」开始。只想会用 bytes 的读者,读 01 全景、02 的前两章、04 和常见坑;想读懂实现的,按顺序读,02 是全卷的重点:两条主线最后都落到 Bytes 的 vtable 上。
💬 关联讨论
发起讨论加载中...