diff --git a/phf/src/map.rs b/phf/src/map.rs index 30fc2648..5f39da1e 100644 --- a/phf/src/map.rs +++ b/phf/src/map.rs @@ -1,10 +1,9 @@ //! An immutable map constructed at compile time. -use core::borrow::Borrow; use core::ops::Index; use core::slice; use core::fmt; use core::iter::IntoIterator; -use phf_shared::{self, PhfHash, HashKey}; +use phf_shared::{self, PhfHash, PhfBorrow, HashKey}; use crate::Slice; /// An immutable map constructed at compile time. @@ -29,7 +28,7 @@ impl fmt::Debug for Map where K: fmt::Debug, V: fmt::Debug { } } -impl<'a, K, V, T: ?Sized> Index<&'a T> for Map where T: Eq + PhfHash, K: Borrow { +impl<'a, K, V, T: ?Sized> Index<&'a T> for Map where T: Eq + PhfHash, K: PhfBorrow { type Output = V; fn index(&self, k: &'a T) -> &V { @@ -51,7 +50,7 @@ impl Map { /// Determines if `key` is in the `Map`. pub fn contains_key(&self, key: &T) -> bool where T: Eq + PhfHash, - K: Borrow + K: PhfBorrow { self.get(key).is_some() } @@ -59,7 +58,7 @@ impl Map { /// Returns a reference to the value that `key` maps to. pub fn get(&self, key: &T) -> Option<&V> where T: Eq + PhfHash, - K: Borrow + K: PhfBorrow { self.get_entry(key).map(|e| e.1) } @@ -70,7 +69,7 @@ impl Map { /// This can be useful for interning schemes. pub fn get_key(&self, key: &T) -> Option<&K> where T: Eq + PhfHash, - K: Borrow + K: PhfBorrow { self.get_entry(key).map(|e| e.0) } @@ -78,7 +77,7 @@ impl Map { /// Like `get`, but returns both the key and the value. pub fn get_entry(&self, key: &T) -> Option<(&K, &V)> where T: Eq + PhfHash, - K: Borrow + K: PhfBorrow { if self.disps.len() == 0 { return None; } //Prevent panic on empty map let hashes = phf_shared::hash(key, &self.key); diff --git a/phf/src/set.rs b/phf/src/set.rs index 9588d4a7..1de6536f 100644 --- a/phf/src/set.rs +++ b/phf/src/set.rs @@ -1,9 +1,10 @@ //! An immutable set constructed at compile time. -use core::borrow::Borrow; use core::iter::IntoIterator; use core::fmt; -use crate::{map, Map, PhfHash}; +use phf_shared::{PhfBorrow, PhfHash}; + +use crate::{map, Map}; /// An immutable set constructed at compile time. /// @@ -40,7 +41,7 @@ impl Set { /// This can be useful for interning schemes. pub fn get_key(&self, key: &U) -> Option<&T> where U: Eq + PhfHash, - T: Borrow + T: PhfBorrow { self.map.get_key(key) } @@ -48,7 +49,7 @@ impl Set { /// Returns true if `value` is in the `Set`. pub fn contains(&self, value: &U) -> bool where U: Eq + PhfHash, - T: Borrow + T: PhfBorrow { self.map.contains_key(value) } @@ -61,7 +62,7 @@ impl Set { } } -impl Set where T: Eq + PhfHash { +impl Set where T: Eq + PhfHash + PhfBorrow { /// Returns true if `other` shares no elements with `self`. pub fn is_disjoint(&self, other: &Set) -> bool { !self.iter().any(|value| other.contains(value)) diff --git a/phf_codegen/test/src/lib.rs b/phf_codegen/test/src/lib.rs index 0f5ffcad..b62605c8 100644 --- a/phf_codegen/test/src/lib.rs +++ b/phf_codegen/test/src/lib.rs @@ -51,6 +51,14 @@ mod test { assert_eq!("a", UNICASE_MAP[&UniCase::new("abc")]); assert_eq!("b", UNICASE_MAP[&UniCase::new("DEf")]); assert!(!UNICASE_MAP.contains_key(&UniCase::new("XyZ"))); + + // allow lookup with non-static slices + let local_str_1 = "AbC".to_string(); + let local_str_2 = "abc".to_string(); + let local_str_3 = "DEf".to_string(); + assert_eq!("a", UNICASE_MAP[&UniCase::new(&*local_str_1)]); + assert_eq!("a", UNICASE_MAP[&UniCase::new(&*local_str_2)]); + assert_eq!("b", UNICASE_MAP[&UniCase::new(&*local_str_3)]); } #[test] diff --git a/phf_shared/src/lib.rs b/phf_shared/src/lib.rs index 3bd017b9..de51c882 100644 --- a/phf_shared/src/lib.rs +++ b/phf_shared/src/lib.rs @@ -83,6 +83,49 @@ pub trait FmtConst { fn fmt_const(&self, f: &mut fmt::Formatter) -> fmt::Result; } +/// Identical to `std::borrow::Borrow` except omitting blanket impls to facilitate other +/// borrowing patterns. +/// +/// The same semantic requirements apply: +/// +/// > In particular `Eq`, `Ord` and `Hash` must be equivalent for borrowed and owned values: +/// `x.borrow() == y.borrow()` should give the same result as `x == y`. +/// +/// (This crate's API only requires `Eq` and `PhfHash`, however.) +/// +/// ### Motivation +/// The conventional signature for lookup methods on collections looks something like this: +/// +/// ```rust,ignore +/// impl Map where K: PhfHash + Eq { +/// fn get(&self, key: &T) -> Option<&V> where T: PhfHash + Eq, K: Borrow { +/// ... +/// } +/// } +/// ``` +/// +/// This allows the key type used for lookup to be different than the key stored in the map so for +/// example you can use `&str` to look up a value in a `Map`. However, this runs into +/// a problem in the case where `T` and `K` are both a `Foo<_>` type constructor but +/// the contained type is different (even being the same type with different lifetimes). +/// +/// The main issue for this crate's API is that, with this method signature, you cannot perform a +/// lookup on a `Map, _>` with a `UniCase<&'a str>` where `'a` is not +/// `'static`; there is no impl of `Borrow` that resolves to +/// `impl Borrow> for UniCase<&'static str>` and one cannot be added either because of +/// all the blanket impls. +/// +/// Instead, this trait is implemented conservatively, without blanket impls, so that impls like +/// this may be added. This is feasible since the set of types that implement `PhfHash` is +/// intentionally small. +/// +/// This likely won't be fixable with specialization alone but will require full support for lattice +/// impls since we technically want to add overlapping blanket impls. +pub trait PhfBorrow { + /// Convert a reference to `self` to a reference to the borrowed type. + fn borrow(&self) -> &B; +} + /// Create an impl of `FmtConst` delegating to `fmt::Debug` for types that can deal with it. /// /// Ideally with specialization this could be just one default impl and then specialized where @@ -111,6 +154,33 @@ delegate_debug!(u128); delegate_debug!(i128); delegate_debug!(bool); +/// `impl PhfBorrow for T` +macro_rules! impl_reflexive( + ($($t:ty),*) => ( + $(impl PhfBorrow<$t> for $t { + fn borrow(&self) -> &$t { + self + } + })* + ) +); + +impl_reflexive!(str, char, u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, bool, [u8]); + +#[cfg(feature = "std")] +impl PhfBorrow for String { + fn borrow(&self) -> &str { + self + } +} + +#[cfg(feature = "std")] +impl PhfBorrow<[u8]> for Vec { + fn borrow(&self) -> &[u8] { + self + } +} + #[cfg(feature = "std")] delegate_debug!(String); @@ -142,6 +212,18 @@ impl<'a, T: 'a + FmtConst + ?Sized> FmtConst for &'a T { } } +impl<'a> PhfBorrow for &'a str { + fn borrow(&self) -> &str { + self + } +} + +impl<'a> PhfBorrow<[u8]> for &'a [u8] { + fn borrow(&self) -> &[u8] { + self + } +} + impl PhfHash for str { #[inline] fn phf_hash(&self, state: &mut H) { @@ -187,6 +269,13 @@ impl FmtConst for unicase::UniCase where S: AsRef { } } +#[cfg(feature = "unicase")] +impl<'b, 'a: 'b, S: ?Sized + 'a> PhfBorrow> for unicase::UniCase<&'a S> { + fn borrow(&self) -> &unicase::UniCase<&'b S> { + self + } +} + macro_rules! sip_impl ( (le $t:ty) => ( impl PhfHash for $t { @@ -244,6 +333,12 @@ macro_rules! array_impl ( fmt_array(self, f) } } + + impl PhfBorrow<[$t]> for [$t; $n] { + fn borrow(&self) -> &[$t] { + self + } + } ) );