Maplike - Crate with traits for abstract containers and operations on them

Hello!

I would like to share my crate, maplike. Maplike provides traits for common operations, .get(), .set(), .insert(), .remove(), .push(), .pop(), .into_iter() etc., over container data structures, such as std's Vec, BTreeMap, BTreeSet, HashMap, HashSet and for multiple third-party types, e.g. stable_vec::StableVec, thunderdome::Arena, tinyvec::ArrayVec, tinyvec::TinyVec.

Link: GitHub - mikwielgus/maplike: Traits for abstract containers and operations over them for std and external crates. · GitHub

I developed this library for myself to make it possible to write code that is generic over different collection-like data types. These types all have considerable similarities between their interfaces, but I couldn't find a suitable library with traits to abstract the shared behavior that I needed, so I rolled my own.

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.

I maintain this library and dogfood it in my other two crates:

  • undoredo: Undo/Redo and non-linear history tree using sparse deltas (diffs), snapshots, or commands on arbitrary data structures.
  • dcel: half-edge data structure that is generic over its containers.

Feedback is welcome!

Below are two code examples taken from the readme:

First example. Function that gets the second element of a collection that is generic over `Vec`, array, BTreeMap:

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 `Vec`s, 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([(0, 10), (1, 20)])), Some(&20));use std::collections::BTreeMap; 

Second example. Code that is generic over `Vec`, `tinyvec::ArrayVec`, `tinyvec::TinyVec`:

use maplike::{Clear, Push, Veclike};
use tinyvec::{ArrayVec, TinyVec};

// 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]);

// Works on `tinyvec::ArrayVec`.
let mut array_vec: ArrayVec<[i32; 8]> = ArrayVec::new();
replace_all(&mut array_vec, &[7, 8, 9]);
assert_eq!(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]);use maplike::{Clear, Push, Veclike};

That looks pretty useful.
What would possbily make it even better would be too add an Entry as well. That's a really powerful function on HashMap etc. and could possibly even be applied to Vec with K: usize(??) or by including it in Get and adding an Entry trait for the return type.

Thanks for reminding me of this, traits for the entry interface are definitely something I need to add.

and could possibly even be applied to Vec with K: usize(??)

I don't think creating an Entry interface for Vec itself (as opposed to BTreeMap, HashMap, IndexMap) is a good idea, because if you hit an index higher than .len(), then to be able to insert an element it will be necessary to create a padding of one or more elements (presumably all Default::default()) to fill what is between it and the Vec's back. Having an insert operation insert more than what the caller provided is not what the caller is likely to expect and would be considerably different from what map-like structures do on insert.

But I can imagine StableVec having an Entry interface. In maplike, I already have .insert() implemented for it by first extending it using its .reserve_for(), so perhaps an issue for this could be opened on stable_vec's issue tracker.