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