Skip to main content

Crate maplike

Crate maplike 

Source
Expand description

Repository Docs Crates.io MSRV MIT OR Apache 2.0

§maplike

Rust traits for abstract containers and operations over them.

With this library, you can write code that is generic over various built-in, standard library, and third-party containers, including collections, and primitives. You can have the same code work on both on BTreeMap and HashMap, and in many cases also on Vec, VecDeque, LinkedList, Option, Box, Rc, Arc and more.

See the Supported containers section for a complete list of supported containers.

Basically, this is Python’s collections.abc, but in Rust, and with traits not only for different kinds of containers, but also for each operation. Essentially, every container, including non-collections, is treated as if it was a map. If it is not really a map, then it is treated as if its key type was usize, even when there can be at most only one element. Hence the crate name, maplike.

The traits are implemented for many containers from std and third-party crates. See the Traits section for a list of all available traits.

This library is maintained and champaigned (aka. dogfooded) as a dependency by its authors, who use it for container-generic code in the following crates:

  • undoredo, a versatile crate for implementing Undo/Redo and non-linear history tree using sparse deltas (diffs), snapshots, or commands on arbitrary data structures;
  • multi_bimap, a crate implementing many-to-many bidirectional map using two antiparallel internal containers chosen by the user;
  • rstared, a simple Rust decorator to add a passively listening R-tree (rstar::RTree) to many standard library and third-party collection types.
  • dcel, a crate that implements the half-edge data structure (aka. doubly connected edge list, DCEL) generically over its underlying containers.

This crate is compatible with no_std and serde and contains no unsafe code. MSRV is 1.92.

If you are looking for abstract number traits instead of or in addition to abstract container traits, also check out another crate of ours, numlike.

§Usage

§Adding dependency

First, add maplike as a dependency to your Cargo.toml:

[dependencies]
maplike = { version = "0.16.0", features = ["derive"] }

The derive feature flag is only needed if you want to derive the Keyed trait using derive macro #[derive(Keyed)].

§Usage examples

maplike’s container traits allow you to write functions that are generic over many different collection types. A single collection trait like Get is enough to abstract over Vecs, arrays, and maps alike.

use std::collections::{BTreeMap, HashMap};

use maplike::ops::Get;

// Generic over any collection implementing the `Get` trait.
fn get_second_element<C: Get<usize>>(collection: &C) -> Option<&C::Value> {
    collection.get(&1)
}

// `get_second_element()` works for `Vec`s, arrays, `BTreeMap`s, `HashMap`s with
// the very same code.
assert_eq!(get_second_element(&vec![10, 20, 30]), Some(&20));
assert_eq!(get_second_element(&[10, 20, 30]), Some(&20));
assert_eq!(get_second_element(&BTreeMap::from([(0, 10), (1, 20)])), Some(&20));
assert_eq!(get_second_element(&HashMap::from([(0, 10), (1, 20)])), Some(&20));

An abstract container trait can bundle together several traits for container methods together in one short bound. For example, an abstract collection trait Veclike joins together (Get, Set, Push, Pop, Clear, Len, and Index), thus allowing for code that is generic over Vec, VecDeque, smallvec::SmallVec, thin_vec::ThinVec, tinyvec::ArrayVec, and tinyvec::TinyVec.

use maplike::abc::{Keyed, Veclike};
use maplike::ops::{Clear, Push};

// This function is generic over any `Veclike` collection. The `Veclike` bound
// provides `.clear()`, `.push()` and many other methods at once.
fn replace_all<C: Veclike<usize, Value = i32>>(collection: &mut C, values: &[i32]) {
    collection.clear();
    for &value in values {
        collection.push(value);
    }
}

// `replace_all()` now works for any `Veclike` collection.

// Works on `Vec`,
let mut vec = Vec::new();
replace_all(&mut vec, &[1, 2, 3]);
assert_eq!(vec, [1, 2, 3]);
replace_all(&mut vec, &[4, 5, 6]);
assert_eq!(vec, [4, 5, 6]);

#[cfg(feature = "smallvec")]
{
    use smallvec::SmallVec;

    // Works on `smallvec::SmallVec`.
    let mut small_vec: SmallVec<[i32; 8]> = SmallVec::new();
    replace_all(&mut small_vec, &[7, 8, 9]);
    assert_eq!(small_vec.as_slice(), [7, 8, 9]);
}

#[cfg(feature = "thin-vec")]
{
    use thin_vec::ThinVec;

    // Works on `thin_vec::ThinVec`.
    let mut thin_vec = ThinVec::new();
    replace_all(&mut thin_vec, &[7, 8, 9]);
    assert_eq!(thin_vec.as_slice(), [7, 8, 9]);
}

#[cfg(feature = "tinyvec")]
{
    use tinyvec::{ArrayVec, TinyVec};

    // Works on `tinyvec::ArrayVec`.
    let mut tiny_array_vec: ArrayVec<[i32; 8]> = ArrayVec::new();
    replace_all(&mut tiny_array_vec, &[7, 8, 9]);
    assert_eq!(tiny_array_vec.as_slice(), [7, 8, 9]);

    // Works on `tinyvec::TinyVec`.
    let mut tiny_vec: TinyVec<[i32; 8]> = TinyVec::new();
    replace_all(&mut tiny_vec, &[10, 11, 12]);
    assert_eq!(tiny_vec.as_slice(), [10, 11, 12]);
}

// NOTE: `arrayvec::ArrayVec` and `arrayvec::ArrayString` are not `Veclike`
// because they do not implement `Index`.

§Traits

§Operations

This crate provides traits for common operations over map-like, set-like, array-like, and vec-like data structures: .with_one(), .assign(), .contains_key(), .get(), .set(), .modify(), .insert(), .remove(), .swap_remove(), .shift_remove(), .push(), .pop(), .put(), .clear(), .len(), .resize(), .values(), .into_values(), .iter(), and .into_iter().

For bidirectional maps, there are also variants of the get and remove operations by left and right key: .get_by_left(), .get_by_right(), .remove_by_left(), .remove_by_right().

§Entry API

We provide generic Entry API for types that have an Entry API: HashMap, BTreeMap, hashbrown::HashMap, and indexmap::IndexMap.

§Containers

For brevity and convenience, we also provide abstract container traits: Container, Keyed, Scalarlike, and particularly abstract collection traits Maplike, Setlike, Arraylike, and Veclike that join together traits of multiple operations.

§Supported containers

§Standard library

Rust’s standard library containers are supported via built-in convenience implementations:

  • HashMap, gated by the std feature (enabled by default);
  • HashSet, gated by the std feature (enabled by default);
  • BTreeMap, gated by the alloc feature (enabled by default);
  • BTreeSet, gated by the alloc feature (enabled by default);
  • Vec, gated by the alloc feature (enabled by default);
  • VecDeque, gated by the alloc feature (enabled by default);
  • LinkedList, gated by the alloc feature (enabled by default);
  • Box, gated by the alloc feature (enabled by default);
  • Rc and its weak pointer, std::rc::Weak, both gated by the alloc feature (enabled by default);
  • Arc, and its weak pointer, std::sync::Weak, both gated by the std feature (enabled by default);
  • Option, not feature-gated;

§Primitives

All Rust’s scalar types (i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64, char, bool, ()) are supported and treated as single-element, usize-keyed maps.

Rust’s compound types (arrays, tuples, slices) are supported and treated as usize-keyed maps.

§maplike’s types

maplike provides and supports its own generic type, One, for a collection that always has only one element. Think Option, but without None. Or Box, but without pointer indirection, behaving like a collection despite holding a value not reference, allocated on the stack.

Wrap your value in this type if you need to treat it as a single-element, usize-keyed map and your type does not happen to be a Rust primitive.

§Third-party types

In addition to the standard library, maplike has built-in feature-gated trait implementations for data structures from certain external crates:

For some examples of practical use, see the examples directory of the undoredo crate.

§Technical sidenotes

Unlike maps and sets, not all stable vector data structures allow insertion and removal at arbitrary indexes regardless of whether they are vacant, occupied or out of bounds. For StableVec, we managed to implement inserting at out-of-bound indexes by changing the length before insertion using the .reserve_for() method. For thunderdome::Arena, we insert at arbitrary key directly via the .insert_at() method.

For Slab, an interface to insert at an arbitrary key is missing apparently because the freelist Slab uses to keep track of its vacant indexes is only singly-linked, not doubly-linked. Inserting an element at an arbitrary vacant index would require removing that index from the freelist. But since there is no backwards link available at a given key, doing so would require traversing the freelist from the beginning to find the position of the previous node, which would incur a slow O(n) time cost.

For SlotMap and DenseSlotMap, keys are generated by the map on insertion and cannot be chosen by the caller, so there is likewise no way to insert at an arbitrary key.

Because of that, we do not implement Insert for Slab, SlotMap, or DenseSlotMap, unlike other traits (and therefore they are also not Maplike).

§Feature flags

  • derive — Enables the Keyed and Assign derive macros.
  • std (enabled by default) — Disable for no_std compatibility. Implies alloc. Enables slotmap’s std feature if slotmap is enabled.
  • alloc (enabled by default) — Enables convenience implementations for alloc collections (BTreeMap, BTreeSet, LinkedList, Vec, VecDeque).
  • arrayvec — Enables convenience implementations for arrayvec::ArrayVec and arrayvec::ArrayString.
  • bidimap — Enables convenience implementations for bidimap::BiBTreeMap and, additionally under the std feature, bidimap::BiHashMap.
  • geo — Enables convenience implementations for geometry types from the geo/geo-types crates. Implies alloc.
  • hashbrown — Enables convenience implementations for hashbrown::HashMap and hashbrown::HashSet.
  • indexmap — Enables convenience implementations for indexmap::IndexMap and indexmap::IndexSet.
  • rstar — Enables convenience implementations for rstar::RTree.
  • slab — Enables convenience implementations for slab::Slab.
  • slotmap — Enables convenience implementations for slotmap::SlotMap, slotmap::DenseSlotMap, slotmap::SecondaryMap, and, additionally under the std feature, slotmap::SparseSecondaryMap.
  • stable-vec — Enables convenience implementations for stable_vec::StableVec.
  • thunderdome — Enables convenience implementations for thunderdome::Arena.
  • smallvec — Enables convenience implementations for smallvec::SmallVec.
  • thin-vec — Enables convenience implementations for thin_vec::ThinVec.
  • tinyvec — Enables convenience implementations for tinyvec::ArrayVec and tinyvec::TinyVec.
  • serde — Enables serde support. Derives Serialize and Deserialize traits for One.

Modules§

abc
Abstract Base Containers: traits that bundle multiple operations together.
entry
Entry API traits for map-like containers.
iter
Iteration traits for map-like containers.
one
One, a collection that holds always exactly one element.
ops
Individual container operation traits.

Derive Macros§

Keyedderive