diff --git a/benches/bench.rs b/benches/bench.rs index 69eca1b0..772b6687 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -557,7 +557,7 @@ fn remove_ordermap_100_000(b: &mut Bencher) { b.iter(|| { let mut map = map.clone(); for key in &keys { - map.remove(&key).is_some(); + map.remove(key).is_some(); } assert_eq!(map.len(), 0); map diff --git a/src/equivalent.rs b/src/equivalent.rs new file mode 100644 index 00000000..1ec4dd35 --- /dev/null +++ b/src/equivalent.rs @@ -0,0 +1,27 @@ + +use std::borrow::Borrow; + +/// Key equivalence trait. +/// +/// This trait allows hash table lookup to be customized. +/// It has one blanket implementation that uses the regular `Borrow` solution, +/// just like `HashMap` and `BTreeMap` do, so that you can pass `&str` to lookup +/// into a map with `String` keys and so on. +/// +/// # Contract +/// +/// The implementor must hash like `K`, if applicable. +pub trait Equivalent { + /// Compare self to `key` and return `true` if they are equal. + fn equivalent(&self, key: &K) -> bool; +} + +impl Equivalent for Q + where Q: Eq, + K: Borrow, +{ + #[inline] + fn equivalent(&self, key: &K) -> bool { + *self == *key.borrow() + } +} diff --git a/src/lib.rs b/src/lib.rs index 8609bd2e..6e0310df 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,12 +5,12 @@ mod macros; #[cfg(feature = "serde-1")] mod serde; mod util; +mod equivalent; use std::hash::Hash; use std::hash::BuildHasher; use std::hash::Hasher; use std::collections::hash_map::RandomState; -use std::borrow::Borrow; use std::ops::RangeFull; use std::cmp::{max, Ordering}; @@ -19,6 +19,7 @@ use std::mem::{replace}; use std::marker::PhantomData; use util::{second, ptrdistance, enumerate}; +pub use equivalent::Equivalent; fn hash_elem_using(build: &B, k: &K) -> HashValue { let mut h = build.build_hasher(); @@ -824,8 +825,7 @@ impl OrderMap /// /// Computes in **O(1)** time (average). pub fn contains_key(&self, key: &Q) -> bool - where K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, { self.find(key).is_some() } @@ -835,15 +835,13 @@ impl OrderMap /// /// Computes in **O(1)** time (average). pub fn get(&self, key: &Q) -> Option<&V> - where K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, { self.get_pair(key).map(second) } pub fn get_pair(&self, key: &Q) -> Option<(&K, &V)> - where K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, { if let Some((_, found)) = self.find(key) { let entry = &self.entries[found]; @@ -855,8 +853,7 @@ impl OrderMap /// Return item index, key and value pub fn get_pair_index(&self, key: &Q) -> Option<(usize, &K, &V)> - where K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, { if let Some((_, found)) = self.find(key) { let entry = &self.entries[found]; @@ -867,16 +864,14 @@ impl OrderMap } pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> - where K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, { self.get_pair_mut(key).map(second) } pub fn get_pair_mut(&mut self, key: &Q) -> Option<(&mut K, &mut V)> - where K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, { if let Some((_, found)) = self.find(key) { let entry = &mut self.entries[found]; @@ -888,8 +883,7 @@ impl OrderMap pub fn get_pair_index_mut(&mut self, key: &Q) -> Option<(usize, &mut K, &mut V)> - where K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, { if let Some((_, found)) = self.find(key) { let entry = &mut self.entries[found]; @@ -901,12 +895,11 @@ impl OrderMap /// Return probe (indices) and position (entries) fn find(&self, key: &Q) -> Option<(usize, usize)> - where K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, { if self.len() == 0 { return None; } let h = hash_elem_using(&self.hash_builder, key); - self.find_using(h, move |entry| { *entry.key.borrow() == *key }) + self.find_using(h, move |entry| { Q::equivalent(key, &entry.key) }) } /// Remove the key-value pair equivalent to `key` and return @@ -920,8 +913,7 @@ impl OrderMap /// /// Computes in **O(1)** time (average). pub fn swap_remove(&mut self, key: &Q) -> Option - where K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, { self.swap_remove_pair(key).map(second) } @@ -930,8 +922,7 @@ impl OrderMap /// /// Computes in **O(1)** time (average). pub fn remove(&mut self, key: &Q) -> Option - where K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, { self.swap_remove(key) } @@ -944,8 +935,7 @@ impl OrderMap /// /// Return `None` if `key` is not in map. pub fn swap_remove_pair(&mut self, key: &Q) -> Option<(K, V)> - where K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, { let (probe, found) = match self.find(key) { None => return None, @@ -1470,9 +1460,8 @@ impl IntoIterator for OrderMap use std::ops::{Index, IndexMut}; impl<'a, K, V, Q: ?Sized, S> Index<&'a Q> for OrderMap - where K: Eq + Hash, - K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, + K: Hash + Eq, S: BuildHasher, { type Output = V; @@ -1492,9 +1481,8 @@ impl<'a, K, V, Q: ?Sized, S> Index<&'a Q> for OrderMap /// /// You can **not** insert new pairs with index syntax, use `.insert()`. impl<'a, K, V, Q: ?Sized, S> IndexMut<&'a Q> for OrderMap - where K: Eq + Hash, - K: Borrow, - Q: Eq + Hash, + where Q: Hash + Equivalent, + K: Hash + Eq, S: BuildHasher, { /// ***Panics*** if `key` is not present in the map. diff --git a/tests/equivalent_trait.rs b/tests/equivalent_trait.rs new file mode 100644 index 00000000..baa0321e --- /dev/null +++ b/tests/equivalent_trait.rs @@ -0,0 +1,42 @@ + +#[macro_use] extern crate ordermap; + +use ordermap::Equivalent; + +use std::hash::Hash; + +#[derive(Debug, Hash)] +pub struct Pair(pub A, pub B); + +impl PartialEq<(A, B)> for Pair + where C: PartialEq, + D: PartialEq, +{ + fn eq(&self, rhs: &(A, B)) -> bool { + self.0 == rhs.0 && + self.1 == rhs.1 && + true + } +} + +impl Equivalent for Pair + where Pair: PartialEq, + A: Hash + Eq, + B: Hash + Eq, +{ + fn equivalent(&self, other: &X) -> bool { + *self == *other + } +} + +#[test] +fn test_lookup() { + let s = String::from; + let map = ordermap! { + (s("a"), s("b")) => 1, + (s("a"), s("x")) => 2, + }; + + assert!(map.contains_key(&Pair("a", "b"))); + assert!(!map.contains_key(&Pair("b", "a"))); +}