diff --git a/src/lib.rs b/src/lib.rs index 5428f111..08c823f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,12 +3,12 @@ mod macros; mod util; +mod lookup; use std::hash::Hash; use std::hash::BuildHasher; use std::hash::Hasher; use std::collections::hash_map::RandomState; -use std::borrow::Borrow; use std::cmp::max; use std::fmt; @@ -16,6 +16,7 @@ use std::mem::{replace}; use std::marker::PhantomData; use util::{second, ptrdistance, enumerate}; +pub use lookup::Lookup; fn hash_elem_using(build: &B, k: &K) -> HashValue { let mut h = build.build_hasher(); @@ -787,8 +788,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: Lookup, { self.find(key).is_some() } @@ -798,15 +798,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: Lookup, { self.get_pair(key).map(second) } pub fn get_pair(&self, key: &Q) -> Option<(&K, &V)> - where K: Borrow, - Q: Eq + Hash, + where Q: Lookup, { if let Some((_, found)) = self.find(key) { let entry = &self.entries[found]; @@ -818,8 +816,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: Lookup, { if let Some((_, found)) = self.find(key) { let entry = &self.entries[found]; @@ -830,16 +827,14 @@ impl OrderMap } pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> - where K: Borrow, - Q: Eq + Hash, + where Q: Lookup, { 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: Lookup, { if let Some((_, found)) = self.find(key) { let entry = &mut self.entries[found]; @@ -851,8 +846,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: Lookup, { if let Some((_, found)) = self.find(key) { let entry = &mut self.entries[found]; @@ -864,12 +858,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: Lookup, { 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::equal(key, &entry.key) }) } /// Remove the key-value pair equivalent to `key` and return @@ -883,8 +876,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: Lookup, { self.swap_remove_pair(key).map(second) } @@ -893,8 +885,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: Lookup, { self.swap_remove(key) } @@ -907,8 +898,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: Lookup, { let (probe, found) = match self.find(key) { None => return None, @@ -1306,9 +1296,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: Lookup, + K: Hash + Eq, S: BuildHasher, { type Output = V; @@ -1328,9 +1317,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: Lookup, + K: Hash + Eq, S: BuildHasher, { /// ***Panics*** if `key` is not present in the map. diff --git a/src/lookup.rs b/src/lookup.rs new file mode 100644 index 00000000..dbfa4fd0 --- /dev/null +++ b/src/lookup.rs @@ -0,0 +1,28 @@ + +use std::hash::Hash; +use std::borrow::Borrow; + +/// Key lookup 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`. +pub trait Lookup : Hash { + /// Compare self to `key` and return `true` if they are equal. + fn equal(&self, key: &K) -> bool; +} + +impl Lookup for Q + where Q: Eq + Hash, + K: Borrow, +{ + #[inline] + fn equal(&self, key: &K) -> bool { + *self == *key.borrow() + } +} diff --git a/tests/lookup_trait.rs b/tests/lookup_trait.rs new file mode 100644 index 00000000..3decbc3c --- /dev/null +++ b/tests/lookup_trait.rs @@ -0,0 +1,41 @@ + +#[macro_use] extern crate ordermap; + +use ordermap::{Lookup}; + +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 Lookup for Pair + where Pair: PartialEq, + A: Hash + Eq, + B: Hash + Eq, +{ + fn equal(&self, other: &X) -> bool { + *self == *other + } +} + +#[test] +fn test_lookup() { + let map = ordermap! { + (String::from("a"), String::from("b")) => 1, + (String::from("a"), String::from("x")) => 2, + }; + + assert!(map.contains_key(&Pair("a", "b"))); + assert!(!map.contains_key(&Pair("b", "a"))); +}