maplike 0.11.0

Derive macros for maplike.
Documentation

Docs Crates.io MIT OR Apache 2.0

maplike

Traits for abstract containers and operations over them.

This crate provides traits for common operations over map-like, set-like, and vec-like data structures: get, set, modify, insert, remove, push, pop, clear, len, assign, and into_iter.

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

For brevity and convenience, we also provide Scalarlike, Maplike, Setlike, Arraylike, and Veclike traits, which represent complete abstract containers that join together traits of multiple operations.

Examples

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

use std::collections::BTreeMap;

use maplike::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 vectors, arrays, and maps 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([(0usize, 10), (1usize, 20)])), Some(&20));

An abstract container trait like Veclike bundles several traits (Get, Set, Push, Pop, Clear, Len, and Index) together behind one bound:

use maplike::{Clear, Push, Veclike};

// 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` works for any `Veclike` collection, such as `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]);

Supported collections

Standard library

Rust's standard library collections 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, not feature-gated;
  • BTreeSet, not feature-gated;
  • Vec, not feature-gated, but does not support stable removal.

Third-party types

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

For examples, see examples directory of the undoredo crate.

Unsupported collections

Standard library's VecDeque is unsupported.

Among stable vector data structures, Slab, SlotMap, generational-arena cannot be supported because they lack interfaces for insertion at an arbitrary key.

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. Collections for which we could not achieve this are documented in the section below.

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 an overly slow O(n) time cost.