1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
//! A fast two-way bijective map.
//!
//! A bimap is a [bijective map] between values of type `L`, called left values,
//! and values of type `R`, called right values. This means every left value is
//! associated with exactly one right value and vice versa. Compare this to a
//! [`HashMap`] or [`BTreeMap`], where every key is associated with exactly one
//! value but a value can be associated with more than one key.
//!
//! This crate provides two kinds of bimap: a [`BiHashMap`] and a
//! [`BiBTreeMap`]. Internally, each one is composed of two maps, one for the
//! left-to-right direction and one for right-to-left. As such, the big-O
//! performance of the `get`, `remove`, `insert`, and `contains` methods are the
//! same as those of the backing map.
//!
//! For convenience, the type definition [`BiMap`] corresponds to a `BiHashMap`.
//! If you're using this crate without the standard library, it instead
//! corresponds to a `BiBTreeMap`.
//!
//! # Examples
//!
//! ```
//! use bimap::BiMap;
//!
//! let mut elements = BiMap::new();
//!
//! // insert chemicals and their corresponding symbols
//! elements.insert("hydrogen", "H");
//! elements.insert("carbon", "C");
//! elements.insert("bromine", "Br");
//! elements.insert("neodymium", "Nd");
//!
//! // retrieve chemical symbol by name (left to right)
//! assert_eq!(elements.get_by_left(&"bromine"), Some(&"Br"));
//! assert_eq!(elements.get_by_left(&"oxygen"), None);
//!
//! // retrieve name by chemical symbol (right to left)
//! assert_eq!(elements.get_by_right(&"C"), Some(&"carbon"));
//! assert_eq!(elements.get_by_right(&"Al"), None);
//!
//! // check membership
//! assert!(elements.contains_left(&"hydrogen"));
//! assert!(!elements.contains_right(&"He"));
//!
//! // remove elements
//! assert_eq!(
//! elements.remove_by_left(&"neodymium"),
//! Some(("neodymium", "Nd"))
//! );
//! assert_eq!(elements.remove_by_right(&"Nd"), None);
//!
//! // iterate over elements
//! for (left, right) in &elements {
//! println!("the chemical symbol for {} is {}", left, right);
//! }
//! ```
//!
//! ## Insertion and overwriting
//!
//! Consider the following example:
//!
//! ```
//! use bimap::BiMap;
//!
//! let mut bimap = BiMap::new();
//! bimap.insert('a', 1);
//! bimap.insert('b', 1); // what to do here?
//! ```
//!
//! In order to maintain the bijection, the bimap cannot have both left-right
//! pairs `('a', 1)` and `('b', 1)`. Otherwise, the right-value `1` would have
//! two left values associated with it. Either we should allow the call to
//! `insert` to go through and overwrite `('a', 1)`, or not let `('b', 1)` be
//! inserted at all. This crate allows for both possibilities. To insert with
//! overwriting, use [`insert`], and to insert without overwriting, use
//! [`insert_no_overwrite`]. The return type of `insert` is the `enum`
//! [`Overwritten`], which indicates what values, if any, were overwritten; the
//! return type of `insert_no_overwrite` is a `Result` indicating if the
//! insertion was successful.
//!
//! This is especially important when dealing with types that can be equal while
//! having different data. Unlike a `HashMap` or `BTreeMap`, which [doesn't
//! update an equal key upon insertion], a bimap updates both the left values
//! and the right values.
//!
//! ```
//! use bimap::{BiMap, Overwritten};
//! use std::cmp::Ordering;
//! use std::hash::{Hash, Hasher};
//!
//! #[derive(Clone, Copy, Debug)]
//! struct Foo {
//! important: char,
//! unimportant: u32,
//! }
//!
//! // equality only depends on the important data
//! impl PartialEq for Foo {
//! fn eq(&self, other: &Foo) -> bool {
//! self.important == other.important
//! }
//! }
//!
//! impl Eq for Foo {}
//!
//! impl PartialOrd for Foo {
//! fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
//! Some(self.cmp(other))
//! }
//! }
//!
//! // ordering only depends on the important data
//! impl Ord for Foo {
//! fn cmp(&self, other: &Foo) -> Ordering {
//! self.important.cmp(&other.important)
//! }
//! }
//!
//! // hash only depends on the important data
//! impl Hash for Foo {
//! fn hash<H: Hasher>(&self, state: &mut H) {
//! self.important.hash(state);
//! }
//! }
//!
//! // create two Foos that are equal but have different data
//! let foo1 = Foo {
//! important: 'a',
//! unimportant: 1,
//! };
//! let foo2 = Foo {
//! important: 'a',
//! unimportant: 2,
//! };
//! assert_eq!(foo1, foo2);
//!
//! // insert both Foos into a bimap
//! let mut bimap = BiMap::new();
//! bimap.insert(foo1, 99);
//! let overwritten = bimap.insert(foo2, 100);
//!
//! // foo1 is overwritten and returned
//! match overwritten {
//! Overwritten::Left(foo, 99) => assert_eq!(foo.unimportant, foo1.unimportant),
//! _ => unreachable!(),
//! };
//!
//! // foo2 is in the bimap
//! assert_eq!(
//! bimap.get_by_right(&100).unwrap().unimportant,
//! foo2.unimportant
//! );
//! ```
//!
//! Note that the `FromIterator` and `Extend` implementations for both
//! `BiHashMap` and `BiBTreeMap` use the `insert` method internally, meaning
//! that values from the original iterator/collection can be silently
//! overwritten.
//!
//! ```
//! use bimap::BiMap;
//! use std::iter::FromIterator;
//!
//! // note that both 'b' and 'c' have the right-value 2
//! let mut bimap = BiMap::from_iter(vec![('a', 1), ('b', 2), ('c', 2)]);
//!
//! // ('b', 2) was overwritten by ('c', 2)
//! assert_eq!(bimap.len(), 2);
//! assert_eq!(bimap.get_by_left(&'b'), None);
//! assert_eq!(bimap.get_by_left(&'c'), Some(&2));
//! ```
//!
//! ## `no_std` compatibility
//!
//! This crate can be used without the standard library when the `std` feature
//! is disabled. If you choose to do this, only `BiBTreeMap` is available, not
//! `BiHashMap`.
//!
//! ## serde compatibility
//!
//! When the `serde` feature is enabled, implementations of `Serialize` and
//! `Deserialize` are provided for [`BiHashMap`] and [`BiBTreeMap`], allowing
//! them to be serialized or deserialized painlessly. See the [`serde`] module
//! for examples and more information.
//!
//! [bijective map]: https://en.wikipedia.org/wiki/Bijection
//! [doesn't update an equal key upon insertion]:
//! https://doc.rust-lang.org/std/collections/index.html#insert-and-complex-keys
//! [`HashMap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
//! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
//! [`insert`]: BiHashMap::insert
//! [`insert_no_overwrite`]: BiHashMap::insert_no_overwrite
// Document everything!
#![deny(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]
// Necessary to support no_std setups
#[allow(unused_imports)]
#[macro_use]
extern crate alloc;
mod mem;
pub mod btree;
pub use btree::BiBTreeMap;
#[cfg(feature = "std")]
pub mod hash;
#[cfg(feature = "std")]
pub use hash::BiHashMap;
/// Type definition for convenience and compatibility with older versions of
/// this crate.
#[cfg(feature = "std")]
pub type BiMap<L, R> = BiHashMap<L, R>;
/// Type definition for convenience and compatibility with older versions of
/// this crate.
#[cfg(not(feature = "std"))]
pub type BiMap<L, R> = BiBTreeMap<L, R>;
#[cfg(all(feature = "serde", feature = "std"))]
pub mod serde;
/// The previous left-right pairs, if any, that were overwritten by a call to
/// the [`insert`](BiHashMap::insert) method of a bimap.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Overwritten<L, R> {
/// Neither the left nor the right value previously existed in the bimap.
Neither,
/// The left value existed in the bimap, and the previous left-right pair is
/// returned.
Left(L, R),
/// The right value existed in the bimap, and the previous left-right pair
/// is returned.
Right(L, R),
/// The left-right pair already existed in the bimap, and the previous
/// left-right pair is returned.
Pair(L, R),
/// Both the left and the right value existed in the bimap, but as part of
/// separate pairs. The first tuple is the left-right pair of the
/// previous left value, and the second is the left-right pair of the
/// previous right value.
Both((L, R), (L, R)),
}
impl<L, R> Overwritten<L, R> {
/// Returns a boolean indicating if the `Overwritten` variant implies any
/// values were overwritten.
///
/// This method is `true` for all variants other than `Neither`.
///
/// # Examples
///
/// ```
/// use bimap::{BiMap, Overwritten};
///
/// let mut bimap = BiMap::new();
/// assert!(!bimap.insert('a', 1).did_overwrite());
/// assert!(bimap.insert('a', 2).did_overwrite());
/// ```
pub fn did_overwrite(&self) -> bool {
!matches!(self, Overwritten::Neither)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn did_overwrite() {
assert_eq!(Overwritten::<char, i32>::Neither.did_overwrite(), false);
assert_eq!(Overwritten::Left('a', 1).did_overwrite(), true);
assert_eq!(Overwritten::Right('a', 1).did_overwrite(), true);
assert_eq!(Overwritten::Pair('a', 1).did_overwrite(), true);
assert_eq!(Overwritten::Both(('a', 1), ('b', 2)).did_overwrite(), true);
}
}