ADR-373: A Bytes Carries What Owns Its Memory; a ref Bytes That Is Only Read Takes Any Run of Bytes

Status: Accepted Date: October 19, 2026 Answers: #718, a Mapped handed where a Bytes is taken, and the copy #701 part 3 asks about, a Vec[u8] that becomes a Bytes Refines: ADR-283 D20 (a Bytes is one shared, immutable buffer; building one is the one copy) Related: ADR-016 D5 (Mapped derefs to Bytes), Part I 2.6, 6.6, Part III 17.2

1. Question

fn lines(b: ref Bytes) -> i64 { … }

let m = fs::map("in.txt", fs::Root::Anywhere)
lines(m)                                       // a mapping, read
http::Server(ROUTES; state: [m])               // a mapping, kept as a body
let b = Bytes(v)                               // a Vec[u8], handed over

Bytes is the language’s run of bytes that several holders share. ADR-016 D5 made a mapped file one, and Part III 17’s server answers with a mapped page as a body that costs a count per request. A Bytes that is one heap buffer and nothing else can hold neither, and turning a Vec[u8] into one copies every byte.

2. How others do it

3. Decision

D1 — A Bytes reads the same whatever owns its memory.

A Bytes holds a run of bytes and a counted handle on what owns them: memory it allocated, a Vec[u8] handed over, or a mapped file. Reading - b[i], cuts, len, find, the word reads - is the same for each and costs the same: where the bytes are and how many is in the value itself. Handing one on is a count. The memory lives as long as any Bytes or view that needs it.

D2 — Bytes(v) takes a Vec[u8] over and copies nothing.

A program builds a Bytes by calling the type with a Vec[u8], which it hands over. The vector’s memory becomes the buffer. fs::read makes its Bytes the same way.

D3 — A Mapped is a Bytes, by reference and by value.

Where a Bytes is taken - a ref Bytes parameter, a Bytes that is kept, a body, a field, an element - a Mapped may be given. It is the mapping, at a count; nothing is copied, and the file stays mapped while any holder lives.

D4 — A ref Bytes that is only read takes any run of bytes.

A parameter b: ref Bytes whose function only reads b (it does not keep it, return it or hand it to something that keeps it) is lowered to a plain run of bytes, the slice Zig would take. Any Bytes, a Mapped, and the bytes of a text (s.bytes) reach it without touching a count. Where the function keeps b, it takes the Bytes with its owner (D1).

4. Rejected alternatives

5. Costs

A Bytes value grows from a pointer and a length (16 bytes on a 64-bit target) to a pointer, a length and a handle on the owner (about 32). D4 keeps that off the common path: a function that only reads takes the 16-byte run. What it costs a program that holds many small Bytes in a list is a measurement owed before the type is changed.

This site is open source. Improve this page.