Skip to main content

MapStorage

Trait MapStorage 

Source
pub trait MapStorage {
    type Key;
    type Value;

    // Required methods
    fn storage_len(&self) -> usize;
    fn storage_insert(&mut self, key: Self::Key, value: Self::Value);
    fn storage_clear(&mut self);
    fn storage_iter<'a>(
        &'a self,
    ) -> impl Iterator<Item = (&'a Self::Key, &'a Self::Value)>
       where Self::Key: 'a,
             Self::Value: 'a;
}
Expand description

The owned collection backing a proto map<K, V> field.

The default is the __private::HashMap alias above; buffa_build’s map_type knob can instead select the buffa-provided BTreeMap (deterministic iteration order, no extra dependency) or a custom map. The five runtime helpers in this module need only three operations from the container — count entries, borrow-iterate entries, and insert one entry — so this trait captures that minimal surface and lets the helpers stay generic over which map a field uses rather than hard-coding __private::HashMap.

The wire format is identical regardless of the container; only the in-memory owned type changes, and view types are unaffected.

It is re-exported at the crate root, so downstream code can write use buffa::MapStorage; (the longer buffa::map_codec::MapStorage path also works).

§Sealing

Unlike [MapCodec] / [MapValueDecode], this trait is not sealed. Those traits guard wire-format invariants the type system cannot express, so they must stay closed. MapStorage carries no wire invariant: a buggy impl can at worst iterate in an unusual order (already permitted for proto maps) or drop the consumer’s own entries. Leaving it open lets a downstream crate wrap a foreign map (e.g. indexmap::IndexMap) in a crate-local newtype and implement MapStorage on it, exactly as the orphan rule requires. The wire-format seal is orthogonal and unaffected.

§Contract

  • storage_insert is last-write-wins, matching proto map-merge semantics (a later entry for an existing key replaces the earlier value).
  • storage_iter and storage_len must agree: the iterator yields exactly storage_len() entries.

The key/value types are associated types (Key / Value), not trait parameters, and the per-collection key bound (Eq + Hash for HashMap, Ord for BTreeMap) lives on each impl. The generic helpers bound only C: MapStorage<Key = …, Value = …> and never name a key bound themselves. Because the key/value are associated, a type implements MapStorage at most once, so the container — and its key and value types — resolve unambiguously everywhere the trait is used as a bound.

§Requirements (for a custom map)

This is the canonical list of what a custom map must provide. A custom map is always a crate-local newtype (the orphan rule blocks implementing the buffa-owned reflection / serde traits on a foreign map), and must implement:

  • MapStorage itself (this trait), naming its Key / Value.
  • Default + Clone + PartialEq + Debug — the generated owned message derives these, so its map field must support them.
  • FromIterator<(Key, Value)> — the view→owned conversion .collect()s entries into the owned map.
  • buffa_descriptor’s ReflectMap under the reflection / vtable path — not derivable, but a BTreeMap/HashMap-backed newtype can delegate to the inner map’s impl. This requirement is std-only (vtable reflection requires std), so a no_std build never needs it.
  • arbitrary::Arbitrary under the arbitrary feature (derivable on a newtype).
  • serde::Serialize / Deserialize under a JSON-enabled build. The proto3 JSON codec drives serialization through this trait, so every proto map key/value type is supported regardless of the container — there is no string-key or scalar-value restriction. (When a newtype wraps the inner map in a single field, a derived Serialize / Deserialize already routes transparently to the inner map; the with-modules call this trait’s methods directly rather than the newtype’s serde, so even that is only needed when the field type bypasses a with-module.)

The buffa-provided HashMap and BTreeMap already satisfy all of these, so selecting BTreeMap needs no consumer code.

This trait is used only as a generic bound; it is not object-safe (the storage_iter RPITIT precludes dyn MapStorage).

Required Associated Types§

Source

type Key

The map key type.

Source

type Value

The map value type.

Required Methods§

Source

fn storage_len(&self) -> usize

Number of entries.

Source

fn storage_insert(&mut self, key: Self::Key, value: Self::Value)

Insert one entry (last-write-wins, matching proto map-merge semantics).

Source

fn storage_clear(&mut self)

Remove all entries (the field’s cleared / default state), retaining capacity where the underlying type allows. Invoked by the generated message clear().

Source

fn storage_iter<'a>( &'a self, ) -> impl Iterator<Item = (&'a Self::Key, &'a Self::Value)>
where Self::Key: 'a, Self::Value: 'a,

Borrow-iterate entries in the container’s native order.

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<K: Eq + Hash, V, S: BuildHasher + Default> MapStorage for HashMap<K, V, S>

Source§

type Key = K

Source§

type Value = V

Source§

fn storage_len(&self) -> usize

Source§

fn storage_insert(&mut self, key: K, value: V)

Source§

fn storage_clear(&mut self)

Source§

fn storage_iter<'a>(&'a self) -> impl Iterator<Item = (&'a K, &'a V)>
where K: 'a, V: 'a,

Source§

impl<K: Ord, V> MapStorage for BTreeMap<K, V>

Source§

type Key = K

Source§

type Value = V

Source§

fn storage_len(&self) -> usize

Source§

fn storage_insert(&mut self, key: K, value: V)

Source§

fn storage_clear(&mut self)

Source§

fn storage_iter<'a>(&'a self) -> impl Iterator<Item = (&'a K, &'a V)>
where K: 'a, V: 'a,

Implementors§