equivalent
Lightweight Rust traits, `Equivalent` and `Comparable`, that let map implementations compare a borrowed lookup key against a differently-typed stored key.
Repository Health
Technical Analysis
equivalent is a tiny, dependency-free Rust crate that defines two traits, Equivalent and Comparable, for comparing a lookup key against a differently-typed stored key inside map implementations. It exists because the standard library’s Borrow trait can only express a lookup type when the stored key can literally hand out a &Q reference, which breaks down for structural types such as tuples or custom reference-wrapping structs.
The crate ships blanket implementations so any type that already implements Eq/Ord and stands in a Borrow relationship with the stored key gets Equivalent/Comparable for free, while custom map crates (most notably indexmap, which originally hosted these traits before they were split out) provide their own implementations for cases the blanket impl can’t cover. It is #![no_std], has zero dependencies, and targets an unusually low MSRV of Rust 1.6, making it safe to add to almost any dependency tree.
What You Get
- Two focused traits,
EquivalentandComparable, each with full API documentation. - Blanket implementations covering the common
Eq/OrdplusBorrowcase out of the box, so most types need no manual impl. - A worked example, compiled and run as a doctest, showing a tuple-comparison implementation the standard library’s
Borrowcan’t express. - A dependency-free,
#![no_std]-compatible crate that adds negligible build time or binary size to a project.
Common Use Cases
- Implementing heterogeneous lookup for custom hash-map or ordered-map types.
- Allowing
&strlookups into maps keyed byString(and similar borrowed/owned key pairs). - Building composite lookup keys, such as tuple- or struct-of-references types, that can’t satisfy a plain
Borrowbound. - Adding key-comparison support to
no_stdcollection crates without pulling in extra dependencies.
Under The Hood
Architecture
The crate is deliberately minimal: a single 114-line src/lib.rs defines Equivalent<K> with one method, equivalent(&self, key: &K) -> bool, and Comparable<K>: Equivalent<K> with compare(&self, key: &K) -> Ordering. Each trait carries a blanket implementation — impl<Q: Eq, K: Borrow<Q>> Equivalent<K> for Q and the Ord-based equivalent for Comparable — so any type already satisfying the standard Borrow relationship implements both traits automatically, and third-party map crates only need to add their own impl for the structural cases the blanket can’t reach (demonstrated in the crate’s doc example with a Pair<&A, &B> type compared against a (C, D) key). There is no runtime state, no I/O, and no allocation anywhere in the crate.
Tech Stack
The Cargo.toml declares zero [dependencies] and #![no_std] at the crate root, so the entire implementation is built on core::borrow::Borrow and core::cmp::Ordering alone. The declared rust-version is 1.6, an exceptionally old MSRV for a modern crate, and CI (.github/workflows/ci.yml) verifies cargo build/cargo test against that MSRV alongside stable, beta, and nightly toolchains on GitHub Actions using dtolnay/rust-toolchain. There is no build step beyond standard cargo build — no codegen, no feature flags, no platform-specific branches.
Code Quality
There is no #[cfg(test)] unit-test module in the crate; the only executable test coverage is the single doctest embedded in the top-level //! documentation, exercised by cargo test --doc in CI. Because the crate does no I/O and returns no fallible results, there is no error-handling surface to assess. Naming is idiomatic and mirrors the standard library it complements (equivalent/compare echoing PartialEq::eq/Ord::cmp), and type safety comes entirely from Rust’s generic trait-bound system (Q: ?Sized, blanket impls) rather than runtime checks. The CI workflow runs build and test across four toolchains but has no separate clippy or rustfmt job.
What Makes It Unique
The crate’s specific contribution is letting a lookup type that has no Borrow<Q> relationship to the stored key — most notably structural types like tuples or reference-wrapping structs — still participate in map key comparison, something the standard library’s Borrow-based HashMap/BTreeMap lookup can’t do. This is a narrow, well-established pattern rather than a novel one: the traits were originally part of the indexmap crate and were later extracted into this standalone crate specifically so other map implementations could depend on them without pulling in all of indexmap.