Here is a more concrete example. I'm still new to Rust, so the code may be not easy to read. I tried to make it short, but it still remains long, so excuse me first.
use std::hash::{Hash, Hasher};
use std::fmt::Debug;
use std::collections::HashMap;
struct DataSave {
save_i32: HashMap<Box<dyn Calc<Output = i32>>, i32>,
save_string: HashMap<Box<dyn Calc<Output = String>>, String>,
}
trait Calc: CalcId {
type Output;
fn check_the_calc(&self, data_save: &DataSave);
fn to_box(&self) -> Box<dyn Calc<Output = Self::Output>>
where
Self: Clone + 'static,
{
Box::new(self.clone())
}
}
trait ContainedIn<T> {
fn contained_in(&self, data_save: &DataSave) -> bool;
}
impl<T> ContainedIn<i32> for T
where
T: Calc<Output = i32> + Clone + 'static,
{
fn contained_in(&self, data_save: &DataSave) -> bool {
data_save.save_i32.contains_key(&self.to_box())
}
}
impl<T> ContainedIn<String> for T
where
T: Calc<Output = String> + Clone + 'static,
{
fn contained_in(&self, data_save: &DataSave) -> bool {
data_save.save_string.contains_key(&self.to_box())
}
}
impl DataSave {
fn contains_key<T: ContainedIn<N>, N>(&self, k: &T) -> bool {
k.contained_in(self)
}
}
// this part is just for a Box<dyn Calc<Output = T>> can be contained in a HashMap as key
// may be can be ignored
trait CalcId {
fn id(&self) -> String;
}
impl<T> CalcId for T
where
T: Calc + Debug,
{
fn id(&self) -> String {
format!("{:?}", self)
}
}
impl<T> Hash for Box<dyn Calc<Output = T>> {
fn hash<H>(&self, state: &mut H) where H: Hasher {
self.id().hash(state)
}
}
impl<T> PartialEq for Box<dyn Calc<Output = T>> {
fn eq(&self, other: &Box<dyn Calc<Output = T>>) -> bool {
self.id() == other.id()
}
}
impl<T> Eq for Box<dyn Calc<Output = T>> {}
//--------------------------------------------------------------------------------
Any type implemented Calc
has a method check_chan_calc
, which takes a paramter DataSave
. What the method do is shown on next block code.
And the DataSave
has a method contains_key
, which take a type implemented on ContainedIn
, and return a bool
value. So this contains_key
just work like the same function in HashMap
.
Then I create some types:
#[derive(Debug, Clone, PartialEq, Eq)]
struct OutI32;
#[derive(Debug, Clone, PartialEq, Eq)]
struct OutString;
trait DoHaveyCalc {
type Output;
fn do_havey_calc(&self) -> Self::Output;
}
impl DoHaveyCalc for OutI32 {
type Output = i32;
fn do_havey_calc(&self) -> Self::Output {
1
}
}
impl DoHaveyCalc for OutString {
type Output = String;
fn do_havey_calc(&self) -> Self::Output {
String::from("a")
}
}
impl<T, N, K> Calc for T
where
T: DoHaveyCalc<Output = N> + Debug + Clone + 'static,
T: ContainedIn<K>
/*
I tried this, but doese not work:
T: DoHaveyCalc<Output = N> + Debug + Clone + 'static + Contained<K>,
*/
{
type Output = i32;
fn check_the_calc(&self, data_save: &DataSave) {
if data_save.contains_key(self) {
println!("self in data_save");
} else {
println!("self not in data_save");
}
}
}
Finnally, I try to implement Calc
:
impl<T> Calc for T
where
T: DoHaveyCalc<Output = i32> + Debug + Clone + 'static,
{
type Output = i32;
fn check_the_calc(&self, data_save: &DataSave) {
if data_save.contains_key(self) {
println!("self in data_save");
} else {
println!("self not in data_save");
}
}
}
let data_save = DataSave { save_i32: HashMap::new(), save_string: HashMap::new() };
OutI32.check_the_calc(&data_save)
This works, but I can just implement Calc
on DoHaveyCalc<Output = i32>
. I also need to implement Calc
on DoHaveyCalc<Output = String>
. This is the part I have no idea how to do it.
The code I imagined may look like:
impl<T, N, K> Calc for T
where
T: DoHaveyCalc<Output = N> + Debug + Clone + 'static,
T: ContainedIn<K>
/*
I tried this, but doese not work:
T: DoHaveyCalc<Output = N> + Debug + Clone + 'static + Contained<K>,
*/
{
type Output = i32;
fn check_the_calc(&self, data_save: &DataSave) {
if data_save.contains_key(self) {
println!("self in data_save");
} else {
println!("self not in data_save");
}
}
}