use std::any::{Any, TypeId, type_name};
use std::hash::{Hash, Hasher};
use std::sync::Arc;
#[derive(Clone)]
pub struct StructuralKey {
inner: Arc<dyn ErasedStructuralKey>,
}
impl StructuralKey {
pub fn new<T>(value: T) -> Self
where
T: Eq + Hash + Send + Sync + 'static,
{
Self {
inner: Arc::new(TypedStructuralKey(value)),
}
}
pub fn type_name(&self) -> &'static str {
self.inner.type_name()
}
pub fn value_eq(&self, other: &Self) -> bool {
self.inner.eq_erased(other.inner.as_ref())
}
pub fn hash_value<H: Hasher>(&self, state: &mut H) {
self.inner.hash_erased(state);
}
fn type_id(&self) -> TypeId {
self.inner.erased_type_id()
}
}
impl PartialEq for StructuralKey {
fn eq(&self, other: &Self) -> bool {
self.type_id() == other.type_id() && self.value_eq(other)
}
}
impl Eq for StructuralKey {}
impl Hash for StructuralKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.type_id().hash(state);
self.hash_value(state);
}
}
trait ErasedStructuralKey: Send + Sync {
fn as_any(&self) -> &dyn Any;
fn erased_type_id(&self) -> TypeId;
fn type_name(&self) -> &'static str;
fn eq_erased(&self, other: &dyn ErasedStructuralKey) -> bool;
fn hash_erased(&self, state: &mut dyn Hasher);
}
struct TypedStructuralKey<T>(T);
impl<T> ErasedStructuralKey for TypedStructuralKey<T>
where
T: Eq + Hash + Send + Sync + 'static,
{
fn as_any(&self) -> &dyn Any {
self
}
fn erased_type_id(&self) -> TypeId {
TypeId::of::<T>()
}
fn type_name(&self) -> &'static str {
type_name::<T>()
}
fn eq_erased(&self, other: &dyn ErasedStructuralKey) -> bool {
other
.as_any()
.downcast_ref::<Self>()
.is_some_and(|other| self.0 == other.0)
}
fn hash_erased(&self, mut state: &mut dyn Hasher) {
self.0.hash(&mut state);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::hash_map::DefaultHasher;
fn hash(key: &StructuralKey) -> u64 {
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
hasher.finish()
}
fn hash_value(key: &StructuralKey) -> u64 {
let mut hasher = DefaultHasher::new();
key.hash_value(&mut hasher);
hasher.finish()
}
#[test]
fn equality_is_structural_and_type_sensitive() {
assert!(StructuralKey::new(7_u64) == StructuralKey::new(7_u64));
assert!(StructuralKey::new(7_u64) != StructuralKey::new(8_u64));
assert!(StructuralKey::new(7_u64) != StructuralKey::new(7_i64));
assert!(!StructuralKey::new(7_u64).value_eq(&StructuralKey::new(7_i64)));
}
#[test]
fn hash_collisions_do_not_make_distinct_values_equal() {
#[derive(Eq, PartialEq)]
struct Collision(u8);
impl Hash for Collision {
fn hash<H: Hasher>(&self, state: &mut H) {
0_u8.hash(state);
}
}
let first = StructuralKey::new(Collision(1));
let second = StructuralKey::new(Collision(2));
assert_eq!(hash(&first), hash(&second));
assert!(first != second);
}
#[test]
fn value_only_hash_does_not_add_the_key_type_namespace() {
#[derive(Eq, PartialEq)]
struct First;
#[derive(Eq, PartialEq)]
struct Second;
impl Hash for First {
fn hash<H: Hasher>(&self, state: &mut H) {
0_u8.hash(state);
}
}
impl Hash for Second {
fn hash<H: Hasher>(&self, state: &mut H) {
0_u8.hash(state);
}
}
assert_eq!(
hash_value(&StructuralKey::new(First)),
hash_value(&StructuralKey::new(Second))
);
}
}