pub unsafe trait BorrowedReader: Read {
// Required methods
fn peek_bytes(&self, n: usize) -> Result<&[u8], Error>;
fn advance(&mut self, n: usize) -> Result<(), Error>;
fn position(&self) -> usize;
// Provided method
fn remaining(&self) -> &[u8] ⓘ { ... }
}Expand description
A Read that can hand out borrowed slices of upcoming bytes.
Implemented by &[u8] and SliceReader; used by walker methods
(LeafWalker::with_bytes, MapEntry::with_key_bytes, etc.) to peek at
length-prefixed payloads without materialising them into owned values.
Read itself cannot be peeked — it might be a streaming source like
File or a network socket whose bytes don’t sit in an addressable
buffer. BorrowedReader is the explicit “I am slice-backed” contract;
methods that want zero-copy access opt into it via a trait bound.
§Safety
This trait is unsafe to implement because the crate’s unsafe code
(in particular [crate::optimised::envelope::read_varlen_slice], the
walk_<field> Wire-fast-path in macro-generated walkers, and the
optimised walker constructors more broadly) relies on a stronger
contract than could be expressed in safe Rust:
-
peek_bytes(n)andremainingmust return slices that point into stable, addressable memory — not a transient buffer that could be moved, reallocated, or overwritten by a subsequent call. -
advance(n)must only move the cursor; it must not invalidate, move, or mutate the bytes already returned bypeek_bytesorremaining. In particular, an impl that holds bytes in an internalVec<u8>and refills theVeconadvance(e.g. a buffered file reader) does not satisfy this contract, because previously-returned slice pointers would dangle after the refill. -
Bytes returned by
peek_bytesorremainingmust remain valid for the reader’s lifetime (i.e. for'rwhere the reader is borrowed as&'r mut R), regardless of how manyadvancecalls happen in between, so the unsafe code extending peek lifetimes viaread_borrowed_bytesand the macro-emittedwalk_<field>Wire fast path do not create dangling pointers. -
remaining().len()is monotonically non-increasing underadvance— for any successfuladvancecall,remaining_after.len() <= remaining_before.len()must hold. The macro-emitted Wire fast path computes the consumed byte count asremaining_before.len() - remaining_after.len()and relies on the result being non-negative; an impl that ever growsremainingacross anadvancewould underflow this subtraction. (The macro guards against underflow with achecked_subthat returns a corrupt-impl error rather than triggering UB, but in-crate impls and any reasonable downstream impl must honour this invariant.) The bullet deliberately does not require thatadvance(n)reducesremainingby exactlyn— an impl that coalesces, buffers, or otherwise chooses a larger reduction is still sound for the unsafe code that depends on this contract.Semantic caveat: the UB-safety promise above is only about memory safety. The macro-emitted
walk_<field>Wire fast path additionally interpretsremaining_before.len() - remaining_after.len()as “the number of bytes theskip_indexed_*call consumed”, and hands those bytes toIndexedMapView/IndexedSeqView/IndexedSetViewfor parsing. An impl that reducesremainingby more than whatskip_*actually visited (e.g. by coalescing reads or pre-fetching the next field) would still be UB-safe, but the view would see trailing bytes the skip didn’t consume and misparse the field. The two in-crate impls (&[u8],SliceReader<'a>) advance by exactlyn, so this is dormant in practice — but a downstream impl that wants to be both UB-safe and semantically correct against the macro should advance by exactlynas well.
The two impls in this crate — &[u8] and SliceReader — both
trivially satisfy this contract: peek_bytes and remaining return
slices into a caller-owned buffer that the reader never mutates, and
advance is a pure cursor bump that only ever reduces remaining.
Violating any of these is undefined behaviour, not just a logic bug; the crate’s unsafe code is correct only under this contract.
Required Methods§
Sourcefn peek_bytes(&self, n: usize) -> Result<&[u8], Error>
fn peek_bytes(&self, n: usize) -> Result<&[u8], Error>
Borrow the next n bytes without advancing the cursor.
Returns the slice on success. The slice must point into stable
memory that survives subsequent advance calls — see the trait’s
safety contract.
Sourcefn advance(&mut self, n: usize) -> Result<(), Error>
fn advance(&mut self, n: usize) -> Result<(), Error>
Advance the cursor past n bytes without copying them.
Equivalent to reading n bytes and discarding them, but cheaper
(the bytes are never touched). Returns an error if fewer than
n bytes remain.
Safety: must not invalidate or move bytes returned by previous
peek_bytes calls — see the trait’s safety contract.
Provided Methods§
Sourcefn remaining(&self) -> &[u8] ⓘ
fn remaining(&self) -> &[u8] ⓘ
The unconsumed tail as a borrowed slice.
Returns a view of the bytes the reader has yet to read, tied to the
reader’s borrow. Unlike peek_bytes, the caller
does not have to know the length in advance — and unlike position,
the returned slice is concrete bytes, not a count.
Use case: capture before/after slices around a skip-style call
to recover the field’s exact wire bytes without decoding them:
let before = reader.remaining(); // &[u8] of all unread bytes
some_skip_routine(&mut reader)?; // advances past one logical value
let after = reader.remaining();
let consumed_bytes = &before[..before.len() - after.len()];
// `consumed_bytes` is the just-skipped value's wire bytes, zero-copy.Any impl that is used as the source for an optimised-walker’s
walk_<field> accessor must override this. The default
implementation debug_assert!s on call (panicking in debug builds
to surface the missing override) and returns &[] in release
(which would produce silently-empty field views — equally bad, but
at least not crashing). Both in-crate impls (&[u8] and
SliceReader<'a>) override; any new downstream BorrowedReader
impl should too, even if it only uses the legacy walker paths
(which don’t consult remaining) — the override is cheap and the
foot-gun cost is much higher than the cost of writing it.