It is certainly possible. Signature getequals
fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq,
The problem is to implement a type &Qsuch that (1) (A, B): Borrow<Q>, and also (2) Qimplements Hash + Eq.
To satisfy condition (1), we need to think about how to write
fn borrow(self: &(A, B)) -> &Q
&Q , ! Q, :
impl Q for (A, B)
impl Q for (&A, &B)
Borrow self, - &Q .
:
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::borrow::Borrow;
// See explanation (1).
struct Pair<A, B>(A, B);
struct BorrowedPair<'a, 'b, A: 'a, B: 'b>(&'a A, &'b B);
// See explanation (2).
trait KeyPair<A, B> {
/// Obtains the first element of the pair.
fn a(&self) -> &A;
/// Obtains the second element of the pair.
fn b(&self) -> &B;
/// Checks if this pair is equal to another pair elementwise.
fn eq_pair(&self, a: &A, b: &B) -> bool;
/// Computes the hash of the pair.
fn erased_hash(&self, state: ErasedHasher);
}
// See explanation (3).
impl<'a, A, B> Borrow<KeyPair<A, B> + 'a> for Pair<A, B>
where
A: Eq + Hash + 'a,
B: Eq + Hash + 'a,
{
fn borrow(&self) -> &(KeyPair<A, B> + 'a) {
self
}
}
// See explanation (4).
impl<'a, A: Hash, B: Hash> Hash for (KeyPair<A, B> + 'a) {
fn hash<H: Hasher>(&self, state: &mut H) {
self.erased_hash(ErasedHasher(state));
}
}
impl<'a, A: Eq, B: Eq> PartialEq for (KeyPair<A, B> + 'a) {
fn eq(&self, other: &Self) -> bool {
self.eq_pair(other.a(), other.b())
}
}
impl<'a, A: Eq, B: Eq> Eq for (KeyPair<A, B> + 'a) {}
pub struct Table<A: Eq + Hash, B: Eq + Hash> {
map: HashMap<Pair<A, B>, f64>,
}
impl<A: Eq + Hash, B: Eq + Hash> Table<A, B> {
fn new() -> Self {
Table { map: HashMap::new() }
}
fn get(&self, a: &A, b: &B) -> f64 {
*self.map.get(&BorrowedPair(a, b) as &KeyPair<A, B>).unwrap()
}
fn set(&mut self, a: A, b: B, v: f64) {
self.map.insert(Pair(a, b), v);
}
}
// Boring stuff below.
impl<A, B> KeyPair<A, B> for Pair<A, B>
where
A: Eq + Hash,
B: Eq + Hash,
{
fn a(&self) -> &A {
&self.0
}
fn b(&self) -> &B {
&self.1
}
fn eq_pair(&self, a: &A, b: &B) -> bool {
&self.0 == a && &self.1 == b
}
fn erased_hash(&self, mut state: ErasedHasher) {
self.0.hash(&mut state);
self.1.hash(&mut state);
}
}
impl<'a, 'b, A, B> KeyPair<A, B> for BorrowedPair<'a, 'b, A, B>
where
A: Eq + Hash + 'a,
B: Eq + Hash + 'a,
{
fn a(&self) -> &A {
self.0
}
fn b(&self) -> &B {
self.1
}
fn eq_pair(&self, a: &A, b: &B) -> bool {
self.0 == a && self.1 == b
}
fn erased_hash(&self, mut state: ErasedHasher) {
self.0.hash(&mut state);
self.1.hash(&mut state);
}
}
// This is needed simply because `&mut Hasher` is not a `Hasher`,
// and `hash<H: Hasher>(&self, &mut H)` means we need a Sized `Hasher`.
struct ErasedHasher<'a>(&'a mut Hasher);
impl<'a> Hasher for ErasedHasher<'a> {
fn finish(&self) -> u64 { self.0.finish() }
fn write(&mut self, bytes: &[u8]) { self.0.write(bytes) }
fn write_u8(&mut self, i: u8) { self.0.write_u8(i) }
fn write_u16(&mut self, i: u16) { self.0.write_u16(i) }
fn write_u32(&mut self, i: u32) { self.0.write_u32(i) }
fn write_u64(&mut self, i: u64) { self.0.write_u64(i) }
fn write_usize(&mut self, i: usize) { self.0.write_usize(i) }
fn write_i8(&mut self, i: i8) { self.0.write_i8(i) }
fn write_i16(&mut self, i: i16) { self.0.write_i16(i) }
fn write_i32(&mut self, i: i32) { self.0.write_i32(i) }
fn write_i64(&mut self, i: i64) { self.0.write_i64(i) }
fn write_isize(&mut self, i: isize) { self.0.write_isize(i) }
}
:
Pair BorrowedPair. (A, B) - E0210. , .
KeyPair Q, . impl Eq + Hash for KeyPair, Eq Hash . , a(), b(), eq_pair() erased_hash(), .
Borrow Pair<A, B> KeyPair + 'a. 'a - , Table::get. 'a , a Pair<A, B> - . 'a, 'static, , Borrow , BorrowedPair 'static, , , .
, Eq Hash. , KeyPair + 'a KeyPair ( KeyPair + 'static ).
get(). , , LLVM .
HashMap<(Cow<A>, Cow<B>), f64>. " ", Owned/Borrowed, get(), set().
, , HashMap Hash + Eq, .