Skip to main content

ProtoBytes

Trait ProtoBytes 

Source
pub trait ProtoBytes:
    Clone
    + PartialEq
    + Default
    + Debug
    + Send
    + Sync
    + Deref<Target = [u8]>
    + AsRef<[u8]>
    + From<Vec<u8>> {
    // Required method
    fn from_wire(payload: WirePayload<'_>) -> Result<Self, DecodeError>;

    // Provided method
    fn as_shared(&self) -> Option<Bytes> { ... }
}
Expand description

The bound generated code places on the Rust type used for a proto bytes field.

buffa implements it for the default Vec<u8> and for bytes::Bytes (which decodes zero-copy from a Bytes-backed buffer). Select another representation with buffa_build’s bytes_type / bytes_type_custom. There is intentionally no blanket impl, and a foreign type cannot implement this trait (orphan rule) — wrap it in a local newtype that implements the trait; see examples/custom-types in the buffa repository for the canonical template.

This is the bytes-side twin of ProtoString; the bounds are exactly what generated code requires of a bytes field:

  • from_wire (the required method, below) — the binary decode constructor.
  • Clone + PartialEq + Default + Debug — for the #[derive(...)] and the hand-written Debug impl on message structs, and for clear() (which resets the field to Default rather than relying on a Vec-specific clear, since a substituted type may be immutable).
  • Send + Sync — so a message owning such a field stays Send + Sync.
  • Deref<Target = [u8]> and AsRef<[u8]> — generated code reads the field’s bytes through these bounds: the size pass borrows &[u8] where bytes_encoded_len expects it, and the write pass goes through put_shared_bytes_field / AsSharedBytes, whose blanket impl reads via AsRef<[u8]>.
  • From<Vec<u8>> — used by the JSON and view→owned paths to construct the field from freshly decoded bytes (binary decode uses from_wire instead). Note that From<&[u8]> is deliberately not required: bytes::Bytes implements it only for &'static [u8], so requiring it would exclude Bytes itself.

For the default Vec<u8> representation every conversion is the identity, so the generic path costs nothing relative to the specialized one.

§Contract

The bounds are structural and cannot capture these invariants; an implementation must uphold them:

  • Default is the empty value — generated clear() resets to Default::default() and implicit-presence encoding skips empty values, so a non-empty Default silently drops or corrupts cleared fields.
  • Deref, AsRef, and the constructors observe the same content — encoding borrows via Deref / AsRef and the view / reflect paths read the same way; if they disagree, a value encodes differently than it reads back.
  • from_wire is value-equivalent to From<Vec<u8>> — binary decode uses from_wire while JSON / view→owned use From, so a representation must not transform the bytes in one path but not the other.

§Limitations

  • repeated elements and map<K, bytes> values must be crate-local. A custom type used as the element of a repeated field — or as a map<K, bytes> value — needs codegen-emitted ReflectElement (vtable) and base64 ProtoElemJson (JSON) impls, which the orphan rule permits only when the type is local to the generating crate. A foreign custom type in that position fails to compile — wrap it in a crate-local newtype. Singular, optional, and oneof uses work with a foreign type directly. (A custom bytes map value is honored just like the built-in bytes::Bytes; only the map<bytes, bytes> carve-out keeps Vec<u8> values.)
  • No Arbitrary impl required. Under the arbitrary feature codegen attaches a generic builder, so a custom type needs no native arbitrary::Arbitrary impl.

Required Methods§

Source

fn from_wire(payload: WirePayload<'_>) -> Result<Self, DecodeError>

Construct the representation from a decoded bytes field’s wire payload.

This is the decode constructor: it owns the borrow-vs-own choice. A Bytes-backed representation takes ownership via WirePayload::into_bytes, which is zero-copy only for an Owned payload — today produced only for multi-chunk sources, so a single-chunk source (including a single Bytes buffer) currently yields Borrowed and copies. For a guaranteed zero-copy bytes field use the built-in bytes::Bytes representation; single-chunk-Bytes zero-copy share for custom types is a planned additive enhancement. There is intentionally no blanket impl; the From<Vec<u8>> supertrait remains for the JSON and view→owned paths.

§Errors

The built-in representations are infallible. A representation that enforces additional invariants (e.g. a fixed length) can reject the value with DecodeError::Custom (carrying a static reason), or return any other DecodeError variant.

Provided Methods§

Source

fn as_shared(&self) -> Option<Bytes>

The representation’s bytes as a reference-counted Bytes handle, when one exists.

This is the encode-side hook for segmented sinks: a representation that stores its bytes as (or can cheaply produce) a Bytes returns Some, letting put_shared_bytes_field splice the field into a Rope by reference count.

The default returns None, which means “copy me” — always correct.

§Correctness

The returned handle must view exactly the bytes of self.as_ref(). A violation (stale handle, different slicing, a reused scratch buffer) produces a corrupt encoding — and only when the sink is segmented: contiguous sinks never call the handle, so the bug is invisible in ordinary tests. Implementations overriding this method should test their encode path against a Rope explicitly (a debug_assert in put_shared_bytes_field catches length mismatches, not aliasing).

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementations on Foreign Types§

Source§

impl ProtoBytes for Bytes

Source§

impl ProtoBytes for Vec<u8>

Source§

fn from_wire(payload: WirePayload<'_>) -> Result<Self, DecodeError>

Implementors§