use core::{
any::Any,
hash::{Hash, Hasher},
ops::Deref,
};
mod obj;
pub use obj::Obj;
pub trait AsAny: Any {
fn as_any(&self) -> &dyn Any;
}
impl<T: Any> AsAny for T {
fn as_any(&self) -> &(dyn Any) {
self as &dyn Any
}
}
pub trait EqObj: PartialEqObj {
fn as_eq_object(&self) -> &dyn EqObj;
fn to_eq_object(self) -> Box<dyn EqObj>;
}
impl<T> EqObj for T
where
T: Eq + PartialEqObj,
{
fn as_eq_object(&self) -> &dyn EqObj {
self
}
fn to_eq_object(self) -> Box<dyn EqObj> {
Box::new(self)
}
}
impl_eq! {
Obj<T> where <T: Deref<Target=X>, X: EqObj + ?Sized>,
dyn EqObj,
}
#[macro_export]
macro_rules! impl_eq {
($(
$Type:ty $(where <$(
$G:ident$(:
$($Gb:ident $(<$($GbIn:ident$(=$GbInEq:ty)?)+>)?)?
$(?$Gbq:ident)?
$(
+
$($Gb2:ident $(<$($GbIn2:ident$(=$GbInEq2:ty)?)+>)?)?
$(?$Gbq2:ident)?
)*
)?
),+>)?
),*$(,)?) => {$(
impl$(<$(
$G$(:
$($Gb $(<$($GbIn$(=$GbInEq)?)+>)?)?
$(?$Gbq)?
$(
+
$($Gb2 $({$($GbIn2$(=$GbInEq2:ty)?)+})?)?
$(?$Gbq2)?
)*
)?
),+>)?
Eq for $Type where $Type: 'static {})*
};
}
pub trait PartialEqObj: AsAny {
fn eq_object(&self, other: &dyn PartialEqObj) -> bool;
fn as_partial_eq_object(&self) -> &dyn PartialEqObj;
fn to_partial_eq_object(self) -> Box<dyn PartialEqObj>;
}
impl<T> PartialEqObj for T
where
T: PartialEq + AsAny,
{
fn eq_object(&self, other: &dyn PartialEqObj) -> bool {
match other.as_any().downcast_ref::<Self>() {
Some(other) => self == other,
None => false,
}
}
fn as_partial_eq_object(&self) -> &dyn PartialEqObj {
self
}
fn to_partial_eq_object(self) -> Box<dyn PartialEqObj> {
Box::new(self)
}
}
impl_partial_eq! {
Obj<T> where <T: Deref<Target=X>, X: PartialEqObj + ?Sized>,
dyn PartialEqObj,
dyn EqObj,
}
#[macro_export]
macro_rules! impl_partial_eq {
($(
$Type:ty $(where <$(
$G:ident$(:
$($Gb:ident $(<$($GbIn:ident$(=$GbInEq:ty)?)+>)?)?
$(?$Gbq:ident)?
$(
+
$($Gb2:ident $(<$($GbIn2:ident$(=$GbInEq2:ty)?)+>)?)?
$(?$Gbq2:ident)?
)*
)?
),+>)?
),*$(,)?) => {$(
impl$(<$(
$G$(:
$($Gb $(<$($GbIn$(=$GbInEq)?)+>)?)?
$(?$Gbq)?
$(
+
$($Gb2 $({$($GbIn2$(=$GbInEq2:ty)?)+})?)?
$(?$Gbq2)?
)*
)?
),+>)?
PartialEq for $Type where $Type: 'static {
fn eq(&self, other: &Self) -> bool {
self.deref().eq_object(other.deref().as_partial_eq_object())
}
})*
};
}
pub trait HashObj {
fn hash_object(&self, state: &mut dyn Hasher);
fn as_hash_object(&self) -> &dyn HashObj;
fn to_hash_object(self) -> Box<dyn HashObj>
where
Self: 'static;
}
impl<T: Hash> HashObj for T {
fn hash_object(&self, mut state: &mut dyn Hasher) {
self.hash(&mut state);
}
fn as_hash_object(&self) -> &dyn HashObj {
self
}
fn to_hash_object(self) -> Box<dyn HashObj>
where
Self: 'static,
{
Box::new(self)
}
}
impl_hash! {
Obj<T> where <T: Deref<Target=X>, X: HashObj + ?Sized>,
dyn HashObj,
}
#[macro_export]
macro_rules! impl_hash {
($(
$Type:ty $(where <$(
$G:ident$(:
$($Gb:ident $(<$($GbIn:ident$(=$GbInEq:ty)?)+>)?)?
$(?$Gbq:ident)?
$(
+
$($Gb2:ident $(<$($GbIn2:ident$(=$GbInEq2:ty)?)+>)?)?
$(?$Gbq2:ident)?
)*
)?
),+>)?
),*$(,)?) => {$(
impl$(<$(
$G$(:
$($Gb $(<$($GbIn$(=$GbInEq)?)+>)?)?
$(?$Gbq)?
$(
+
$($Gb2 $({$($GbIn2$(=$GbInEq2:ty)?)+})?)?
$(?$Gbq2)?
)*
)?
),+>)?
std::hash::Hash for $Type {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.deref().hash_object(state);
}
}
)*};
}
#[cfg(test)]
mod test {
use std::collections::hash_map::DefaultHasher;
use super::*;
#[test]
fn eq() {
let x: Box<dyn EqObj> = Box::new(10);
let y: Box<dyn EqObj> = Box::new(10);
let z: Box<dyn EqObj> = Box::new(11);
if x != y {
panic!("should be equal")
}
if x == z {
panic!("should not be equal")
}
}
#[test]
fn hash_works() {
let x: &str = "Hello, World!";
let y: &dyn HashObj = "Hello, World!".as_hash_object();
let z: &dyn HashObj = "banana".as_hash_object();
assert_eq!(hash(x), hash(y));
assert_ne!(hash(y), hash(z));
}
fn hash<T: Hash>(t: T) -> u64 {
let mut hasher = DefaultHasher::new();
t.hash(&mut hasher);
hasher.finish()
}
mod obj_tests {
use crate::*;
trait MyHash: HashObj {}
#[derive(Hash)]
struct MyHashWrapper(Obj<Box<dyn MyHash>>);
impl<T> MyHash for T where T: Hash {}
trait MyPartialEq: PartialEqObj {}
#[derive(PartialEq)]
struct MyPartialEqWrapper(Obj<Box<dyn MyPartialEq>>);
impl<T> MyPartialEq for T where T: PartialEq + 'static {}
trait MyEq: EqObj + PartialEqObj + std::fmt::Debug {}
#[derive(PartialEq, Eq, Debug)]
struct MyEqWrapper(Obj<Box<dyn MyEq>>);
impl<T> MyEq for T where T: Eq + 'static + std::fmt::Debug {}
#[test]
fn hash_obj_works() {
let a = super::hash(0);
let b = super::hash(Box::new(0));
let c = super::hash(Obj(Box::new(0)));
let d = super::hash(MyHashWrapper(Obj(Box::new(0))));
assert_eq!(a, b);
assert_eq!(a, c);
assert_eq!(a, d);
}
#[test]
fn obj_box_dyn_custom_eq() {
assert_eq!(
Obj(Box::new(0) as Box<dyn MyEq>),
Obj(Box::new(0) as Box<dyn MyEq>)
);
assert_ne!(
Obj(Box::new(0) as Box<dyn MyEq>),
Obj(Box::new(1) as Box<dyn MyEq>)
);
}
#[test]
fn wrapped_obj_box_dyn_custom_eq() {
assert_eq!(
MyEqWrapper(Obj(Box::new(0) as Box<dyn MyEq>)),
MyEqWrapper(Obj(Box::new(0) as Box<dyn MyEq>))
);
assert_ne!(
MyEqWrapper(Obj(Box::new(0) as Box<dyn MyEq>)),
MyEqWrapper(Obj(Box::new(1) as Box<dyn MyEq>))
);
}
#[test]
fn obj_box_eq() {
assert_eq!(Obj(Box::new(0)), Obj(Box::new(0)));
assert_ne!(Obj(Box::new(0)), Obj(Box::new(1)));
}
#[test]
fn wrapped_ob_box_eq() {
assert_eq!(MyEqWrapper(Obj(Box::new(0))), MyEqWrapper(Obj(Box::new(0))));
assert_ne!(MyEqWrapper(Obj(Box::new(0))), MyEqWrapper(Obj(Box::new(1))));
}
}
mod impl_tests {
use crate::*;
trait MyTrait: HashObj + EqObj + PartialEqObj {}
impl<T> MyTrait for T where T: Hash + Eq + PartialEq + 'static {}
impl_hash!(dyn MyTrait);
impl_eq!(dyn MyTrait);
impl_partial_eq!(dyn MyTrait);
#[test]
fn box_dyn_custom_eq() {
if Box::new(0) as Box<dyn MyTrait> != Box::new(0) as Box<dyn MyTrait> {
panic!("should be equal");
}
if Box::new(0) as Box<dyn MyTrait> == Box::new(1) as Box<dyn MyTrait> {
panic!("should not be equal");
}
}
}
}