Skip to main content

Message

Trait Message 

Source
pub trait Message:
    DefaultInstance
    + Clone
    + PartialEq
    + Send
    + Sync {
Show 18 methods // Required methods fn compute_size(&self, cache: &mut SizeCache) -> u32; fn write_to(&self, cache: &mut SizeCache, buf: &mut impl BufMut); fn merge_field( &mut self, tag: Tag, buf: &mut impl Buf, depth: u32, ) -> Result<(), DecodeError>; fn clear(&mut self); // Provided methods fn encode(&self, buf: &mut impl BufMut) { ... } fn encode_with_cache(&self, cache: &mut SizeCache, buf: &mut impl BufMut) { ... } fn encoded_len(&self) -> u32 { ... } fn encode_length_delimited(&self, buf: &mut impl BufMut) { ... } fn encode_to_vec(&self) -> Vec<u8> { ... } fn encode_to_bytes(&self) -> Bytes { ... } fn decode(buf: &mut impl Buf) -> Result<Self, DecodeError> where Self: Sized { ... } fn decode_from_slice(data: &[u8]) -> Result<Self, DecodeError> where Self: Sized { ... } fn decode_length_delimited(buf: &mut impl Buf) -> Result<Self, DecodeError> where Self: Sized { ... } fn merge_to_limit( &mut self, buf: &mut impl Buf, depth: u32, limit: usize, ) -> Result<(), DecodeError> { ... } fn merge_group( &mut self, buf: &mut impl Buf, depth: u32, field_number: u32, ) -> Result<(), DecodeError> { ... } fn merge( &mut self, buf: &mut impl Buf, depth: u32, ) -> Result<(), DecodeError> { ... } fn merge_from_slice(&mut self, data: &[u8]) -> Result<(), DecodeError> { ... } fn merge_length_delimited( &mut self, buf: &mut impl Buf, depth: u32, ) -> Result<(), DecodeError> { ... }
}
Expand description

The core trait implemented by all protobuf message types.

This trait is implemented by generated code — you write a .proto file, codegen emits the Rust struct and its Message impl. You should almost never implement this trait by hand.

§Manual implementation is discouraged

The only reason to implement Message yourself is when you need a custom in-memory representation that codegen cannot produce — for example, wrapping a std::ops::Range<i64> as a leaf message so the rest of your code uses the natural Rust type. If you just want a message type, write a .proto file instead.

Manual implementation is intentionally high-friction:

  • You must correctly implement the two-pass serialization contract (compute_size populates the SizeCache in the same traversal order that write_to consumes it).
  • You must implement wire-format decoding in merge_field.
  • You must implement the DefaultInstance supertrait, which provides the lazily-initialized static default that MessageField dereferences to when unset.

If you still need to do this, see the custom types section of the user guide for a complete worked example.

§Serialization model

Serialization is a two-pass process to avoid the exponential-time problem that affects prost with deeply nested messages:

  1. compute_size() — walks the message tree and records the encoded size of every length-delimited sub-message in a SizeCache.
  2. write_to() — walks the tree again, writing bytes and consuming cached sizes for length-prefixed sub-messages.

The provided encode method performs both passes with a fresh SizeCache — most callers use that and never touch the cache directly. compute_size / write_to take the cache explicitly so that manual Message implementations can thread it through nested-message recursion.

§Thread safety

Message requires Send + Sync. Generated structs contain no interior mutability — serialization state lives in the external SizeCache, not in the message — so messages can be placed in an Arc and shared across threads freely. merge requires &mut self, so mutation is exclusive.

Required Methods§

Source

fn compute_size(&self, cache: &mut SizeCache) -> u32

Compute the encoded byte size of this message, recording nested sub-message sizes in cache for write_to to consume.

Most callers should use encode instead, which runs both passes with a fresh cache. Manual Message implementations call this recursively on nested message fields, wrapping each call in SizeCache::reserve / SizeCache::set for length-delimited fields — see the user guide’s custom-types section for the pattern.

§Size limit

The protobuf specification limits messages to 2 GiB. The return type is u32, so messages whose encoded size exceeds u32::MAX (4 GiB) will produce a wrapped (undefined) size and a truncated encoding. Stay well within the 2 GiB spec limit.

Source

fn write_to(&self, cache: &mut SizeCache, buf: &mut impl BufMut)

Write this message’s encoded bytes to a buffer, consuming nested-message sizes from cache (populated by a prior compute_size call on the same cache).

Most callers should use encode instead.

Source

fn merge_field( &mut self, tag: Tag, buf: &mut impl Buf, depth: u32, ) -> Result<(), DecodeError>

Processes a single already-decoded tag and its associated field data from buf.

This is the per-field dispatch method generated for each message type. Both merge_to_limit and merge_group call this in their respective loops.

depth is the remaining nesting budget.

§Errors

Returns a DecodeError if:

  • the buffer is truncated or malformed,
  • a wire-type mismatch is detected for a known field, or
  • the recursion limit is exceeded.
Source

fn clear(&mut self)

Clear all fields to their default values.

Provided Methods§

Source

fn encode(&self, buf: &mut impl BufMut)

Compute size, then write. This is the primary encoding API.

Source

fn encode_with_cache(&self, cache: &mut SizeCache, buf: &mut impl BufMut)

Encode using a caller-supplied SizeCache, for reuse across many encodes in a hot loop. Clears the cache first.

Source

fn encoded_len(&self) -> u32

Compute the encoded byte size of this message.

Walks the message tree, discarding the intermediate SizeCache. If you also intend to encode, prefer encode or encode_to_vec — they do a single size pass and reuse the cache for the write.

Source

fn encode_length_delimited(&self, buf: &mut impl BufMut)

Encode this message as a length-delimited byte sequence.

Source

fn encode_to_vec(&self) -> Vec<u8>

Encode this message to a new Vec<u8>.

Source

fn encode_to_bytes(&self) -> Bytes

Encode this message to a new bytes::Bytes.

Useful when handing off to networking code (hyper, tonic, axum) that expects Bytes frame or body payloads. Works in no_std.

This is equivalent to Bytes::from(self.encode_to_vec()) — both are zero-copy with respect to the encoded bytes — but saves readers from having to know that From<Vec<u8>> for Bytes is zero-copy.

Source

fn decode(buf: &mut impl Buf) -> Result<Self, DecodeError>
where Self: Sized,

Decode a message from a buffer.

Source

fn decode_from_slice(data: &[u8]) -> Result<Self, DecodeError>
where Self: Sized,

Decode a message from a byte slice.

Convenience wrapper around decode that avoids the &mut bytes.as_slice() incantation.

Source

fn decode_length_delimited(buf: &mut impl Buf) -> Result<Self, DecodeError>
where Self: Sized,

Decode a length-delimited message from a buffer.

This is a top-level entry point. It reads a varint length prefix, then decodes using arithmetic bounds checking, calling merge_to_limit with a fresh RECURSION_LIMIT budget. Any sub-messages inside are decoded via merge_length_delimited, which tracks and decrements the budget.

Do not call this method from within a merge_to_limit implementation to decode a nested sub-message field; use merge_length_delimited instead so that the caller’s depth budget is propagated correctly.

Source

fn merge_to_limit( &mut self, buf: &mut impl Buf, depth: u32, limit: usize, ) -> Result<(), DecodeError>

Merge fields from a buffer until buf.remaining() reaches limit.

This is the core decode loop. merge delegates to this with limit = 0 (read until exhausted). merge_length_delimited computes limit from the declared sub-message length and calls this directly.

The caller must ensure limit <= buf.remaining(). The default implementations of merge and merge_length_delimited uphold this invariant.

depth is the remaining nesting budget. Each call to merge_length_delimited decrements it by one before recursing; when it reaches zero the call returns DecodeError::RecursionLimitExceeded.

Source

fn merge_group( &mut self, buf: &mut impl Buf, depth: u32, field_number: u32, ) -> Result<(), DecodeError>

Merges a group-encoded message from buf, reading fields until an EndGroup tag with the given field_number is encountered.

Proto2 groups use StartGroup/EndGroup wire types instead of length-delimited encoding. The opening StartGroup tag has already been consumed by the caller; this method reads the group body and the closing EndGroup tag.

§Errors

Returns a DecodeError if:

  • the buffer is truncated before the EndGroup tag,
  • an EndGroup tag is encountered with a mismatched field number,
  • a wire-type mismatch is detected for a known field, or
  • the recursion limit is exceeded.
Source

fn merge(&mut self, buf: &mut impl Buf, depth: u32) -> Result<(), DecodeError>

Merge fields from a buffer into this message.

Fields that are already set will be overwritten for singular fields, or appended for repeated fields, following standard protobuf merge semantics.

depth is the remaining nesting budget. Each call to merge_length_delimited decrements it by one before recursing; when it reaches zero the call returns DecodeError::RecursionLimitExceeded. Pass RECURSION_LIMIT at the outermost call site, or use the convenience methods (decode, merge_from_slice) which do this automatically.

Source

fn merge_from_slice(&mut self, data: &[u8]) -> Result<(), DecodeError>

Merge fields from a byte slice into this message.

Convenience wrapper around merge that avoids the &mut bytes.as_slice() incantation.

Source

fn merge_length_delimited( &mut self, buf: &mut impl Buf, depth: u32, ) -> Result<(), DecodeError>

Merge fields from a length-delimited sub-message payload into this message.

Reads a varint length prefix, then calls merge_to_limit with an arithmetic bound derived from the declared sub-message length. The buffer type B passes through unchanged at every recursion level, avoiding the E0275 trait-solver recursion limit that occurs with Take<&mut Take<&mut T>> type growth.

Used by generated code when decoding singular MessageField<T> fields — the sub-message is merged into the existing value rather than replaced, per protobuf merge semantics.

depth is the remaining nesting budget passed down from the enclosing merge_to_limit call. This method decrements it by one before calling the inner merge_to_limit; when it reaches zero it returns DecodeError::RecursionLimitExceeded.

Enforces the same 2 GiB safety limit as decode_length_delimited.

§Errors

Returns an error if the buffer is too short, if the declared length exceeds 2 GiB, if the recursion limit is reached, or if the inner merge_to_limit call fails.

Dyn Compatibility§

This trait is not dyn compatible.

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

Implementors§