Skip to content

Commit

Permalink
replace uses of std::borrow::Borrow with new PhfBorrow trait
Browse files Browse the repository at this point in the history
  • Loading branch information
abonander authored and JohnTitor committed Jun 9, 2021
1 parent 9ae1678 commit b2f3a9c
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 12 deletions.
13 changes: 6 additions & 7 deletions 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.
Expand All @@ -29,7 +28,7 @@ impl<K, V> fmt::Debug for Map<K, V> where K: fmt::Debug, V: fmt::Debug {
}
}

impl<'a, K, V, T: ?Sized> Index<&'a T> for Map<K, V> where T: Eq + PhfHash, K: Borrow<T> {
impl<'a, K, V, T: ?Sized> Index<&'a T> for Map<K, V> where T: Eq + PhfHash, K: PhfBorrow<T> {
type Output = V;

fn index(&self, k: &'a T) -> &V {
Expand All @@ -51,15 +50,15 @@ impl<K, V> Map<K, V> {
/// Determines if `key` is in the `Map`.
pub fn contains_key<T: ?Sized>(&self, key: &T) -> bool
where T: Eq + PhfHash,
K: Borrow<T>
K: PhfBorrow<T>
{
self.get(key).is_some()
}

/// Returns a reference to the value that `key` maps to.
pub fn get<T: ?Sized>(&self, key: &T) -> Option<&V>
where T: Eq + PhfHash,
K: Borrow<T>
K: PhfBorrow<T>
{
self.get_entry(key).map(|e| e.1)
}
Expand All @@ -70,15 +69,15 @@ impl<K, V> Map<K, V> {
/// This can be useful for interning schemes.
pub fn get_key<T: ?Sized>(&self, key: &T) -> Option<&K>
where T: Eq + PhfHash,
K: Borrow<T>
K: PhfBorrow<T>
{
self.get_entry(key).map(|e| e.0)
}

/// Like `get`, but returns both the key and the value.
pub fn get_entry<T: ?Sized>(&self, key: &T) -> Option<(&K, &V)>
where T: Eq + PhfHash,
K: Borrow<T>
K: PhfBorrow<T>
{
if self.disps.len() == 0 { return None; } //Prevent panic on empty map
let hashes = phf_shared::hash(key, &self.key);
Expand Down
11 changes: 6 additions & 5 deletions 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.
///
Expand Down Expand Up @@ -40,15 +41,15 @@ impl<T> Set<T> {
/// This can be useful for interning schemes.
pub fn get_key<U: ?Sized>(&self, key: &U) -> Option<&T>
where U: Eq + PhfHash,
T: Borrow<U>
T: PhfBorrow<U>
{
self.map.get_key(key)
}

/// Returns true if `value` is in the `Set`.
pub fn contains<U: ?Sized>(&self, value: &U) -> bool
where U: Eq + PhfHash,
T: Borrow<U>
T: PhfBorrow<U>
{
self.map.contains_key(value)
}
Expand All @@ -61,7 +62,7 @@ impl<T> Set<T> {
}
}

impl<T> Set<T> where T: Eq + PhfHash {
impl<T> Set<T> where T: Eq + PhfHash + PhfBorrow<T> {
/// Returns true if `other` shares no elements with `self`.
pub fn is_disjoint(&self, other: &Set<T>) -> bool {
!self.iter().any(|value| other.contains(value))
Expand Down
8 changes: 8 additions & 0 deletions phf_codegen/test/src/lib.rs
Expand Up @@ -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]
Expand Down
95 changes: 95 additions & 0 deletions phf_shared/src/lib.rs
Expand Up @@ -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<K, V> Map<K, V> where K: PhfHash + Eq {
/// fn get<T: ?Sized>(&self, key: &T) -> Option<&V> where T: PhfHash + Eq, K: Borrow<T> {
/// ...
/// }
/// }
/// ```
///
/// 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<String, _>`. 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<UniCase<&'static str>, _>` with a `UniCase<&'a str>` where `'a` is not
/// `'static`; there is no impl of `Borrow` that resolves to
/// `impl Borrow<UniCase<'a>> 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<B: ?Sized> {
/// 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
Expand Down Expand Up @@ -111,6 +154,33 @@ delegate_debug!(u128);
delegate_debug!(i128);
delegate_debug!(bool);

/// `impl PhfBorrow<T> 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<str> for String {
fn borrow(&self) -> &str {
self
}
}

#[cfg(feature = "std")]
impl PhfBorrow<[u8]> for Vec<u8> {
fn borrow(&self) -> &[u8] {
self
}
}

#[cfg(feature = "std")]
delegate_debug!(String);

Expand Down Expand Up @@ -142,6 +212,18 @@ impl<'a, T: 'a + FmtConst + ?Sized> FmtConst for &'a T {
}
}

impl<'a> PhfBorrow<str> 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<H: Hasher>(&self, state: &mut H) {
Expand Down Expand Up @@ -187,6 +269,13 @@ impl<S> FmtConst for unicase::UniCase<S> where S: AsRef<str> {
}
}

#[cfg(feature = "unicase")]
impl<'b, 'a: 'b, S: ?Sized + 'a> PhfBorrow<unicase::UniCase<&'b S>> for unicase::UniCase<&'a S> {
fn borrow(&self) -> &unicase::UniCase<&'b S> {
self
}
}

macro_rules! sip_impl (
(le $t:ty) => (
impl PhfHash for $t {
Expand Down Expand Up @@ -244,6 +333,12 @@ macro_rules! array_impl (
fmt_array(self, f)
}
}

impl PhfBorrow<[$t]> for [$t; $n] {
fn borrow(&self) -> &[$t] {
self
}
}
)
);

Expand Down

0 comments on commit b2f3a9c

Please sign in to comment.