Search code examples
rustdynamic-dispatch

How to have dynamic Box<dyn Trait<T,S>>?


I have a list of transactions of type T and S. At run time I want to fetch its details. How to have traits for multiple generics types for struct Code? I tried the following code:

    use std::fmt::Debug;
    use std::any::Any;
    
    
    #[derive(Debug)]
    struct TransactionSet<T,S> {
        pub name: String,
        pub key: T,
        pub value: S
    }
    
    trait Details<T,S> {
        fn get_details(&self);
    
        fn get_key(&self)->&T;
    
        fn get_value(&self)->&S;
    }
    
    impl<T: Debug, S: Debug> Details<T,S> for TransactionSet<T, S> {
        fn get_details(&self) {
            println!("{:?} {:?} {:?}",self.name.to_string(),&self.key,&self.value)
        }

        fn get_key(&self)->&T {
            &self.key
        }
    
        fn get_value(&self)->&S {
            &self.value
        }
    
    }
    
        
    fn print_type_of<T>(_: &T) {
        println!("{}", std::any::type_name::<T>())
    }
    
    fn get<T: Any>(value: Box<dyn Any>) -> T {
        let pv = value.downcast().expect("The pointed-to value must be of type T");
        *pv
    }
    
       
    fn main() {
        //let mut vec: Vec<Box<dyn NewTrait<T:Debug,S:Debug>>> = Vec::new(); //Not working
        //  let mut vec: Vec<Box<dyn Details<T: Debug,S: Debug>>> = Vec::new();//Cannot use Details Trait
        let  vec: Vec<Box<dyn Details<_,_>>> = Vec::new();//Cannot use Details Trait
    
        let a1:TransactionSet<String,String> = TransactionSet { name: String::from("Test1"), key: String::from("name"), value: String::from("vinay") };
        let a2:TransactionSet<String,i32> = TransactionSet { name: String::from("Test2"), key: String::from("age"), value: 32_i32 };
        let a3 = TransactionSet { name: String::from("Test3"), key: 1_i32, value: 10_u64 };
        let a4 = TransactionSet { name: String::from("Test4"), key: String::from("isEligibleToVote"), value: true };
    
        //let a2 = ABC::new( String::from("Test2"), String::from("company"),  String::from("supra"));;
    
        vec.push(Box::<TransactionSet<String,String>>::new(a1));
        vec.push(Box::<TransactionSet<String,i32>>::new(a2));
        vec.push(Box::new(a3));
        vec.push(Box::new(a4));
        for v in vec.iter() {
            v.get_details();
            //  print_type_of(v);
    
            println!("Key : {:?}", v.get_key());
            println!("Value : {:?}", v.get_value());
        }
    
    }

Is there any other way I can resolve this? Like Vec<Box<dyn Details<?Unknown,?Unknown>>>. If I remove the generic type for trait Details, I cannot write a getter for it.

Playground Link


Solution

  • You need to use dyn in order to abstract to the trait you need.

    First step is to add types to your trait, it does not need to be generic:

    trait Details {
        type Key;
        type Value;
        fn get_details(&self);
    
        fn get_key(&self) -> &Self::Key;
    
        fn get_value(&self) -> &Self::Value;
    }
    

    Then you can implement the trait over some generic TransactionSet where both T and S implement Debug:

    impl Details for TransactionSet<Box<dyn Debug>, Box<dyn Debug>> {
        type Key = Box<dyn Debug>;
        type Value = Box<dyn Debug>;
        fn get_details(&self) {
            println!(
                "{:?} {:?} {:?}",
                self.name.to_string(),
                &self.key,
                &self.value
            )
        }
        fn get_key(&self) -> &Self::Key {
            &self.key
        }
    
        fn get_value(&self) -> &Self::Value {
            &self.value
        }
    }
    

    Note that the type of your TransactionSet is of TransactionSet<Box<dyn Debug>, Box<dyn Debug>>.

    Here is a full working example:

    use std::any::Any;
    use std::fmt::Debug;
    
    #[derive(Debug)]
    struct TransactionSet<T, S> {
        pub name: String,
        pub key: T,
        pub value: S,
    }
    
    trait Details {
        type Key;
        type Value;
        fn get_details(&self);
    
        fn get_key(&self) -> &Self::Key;
    
        fn get_value(&self) -> &Self::Value;
    }
    
    impl Details for TransactionSet<Box<dyn Debug>, Box<dyn Debug>> {
        type Key = Box<dyn Debug>;
        type Value = Box<dyn Debug>;
        fn get_details(&self) {
            println!(
                "{:?} {:?} {:?}",
                self.name.to_string(),
                &self.key,
                &self.value
            )
        }
        fn get_key(&self) -> &Self::Key {
            &self.key
        }
    
        fn get_value(&self) -> &Self::Value {
            &self.value
        }
    }
    
    fn print_type_of<T>(_: &T) {
        println!("{}", std::any::type_name::<T>())
    }
    
    fn get<T: Any>(value: Box<dyn Any>) -> T {
        let pv = value
            .downcast()
            .expect("The pointed-to value must be of type T");
        *pv
    }
    
    fn main() {
        let mut vec: Vec<Box<dyn Details<Key = _, Value = _>>> = Vec::new(); //Cannot use Details Trait
    
        let a1: TransactionSet<Box<dyn Debug>, Box<dyn Debug>> = TransactionSet {
            name: String::from("Test1"),
            key: Box::new(String::from("name")),
            value: Box::new(String::from("vinay")),
        };
        let a2: TransactionSet<Box<dyn Debug>, Box<dyn Debug>> = TransactionSet {
            name: String::from("Test2"),
            key: Box::new(String::from("age")),
            value: Box::new(32_i32),
        };
        // let a3 = TransactionSet { name: String::from("Test3"), key: 1_i32, value: 10_u64 };
        // let a4 = TransactionSet { name: String::from("Test4"), key: String::from("isEligibleToVote"), value: true };
    
        vec.push(Box::<TransactionSet<Box<dyn Debug>, Box<dyn Debug>>>::new(
            a1,
        ));
        vec.push(Box::<TransactionSet<Box<dyn Debug>, Box<dyn Debug>>>::new(a2));
        // vec.push(Box::new(a3));
        // vec.push(Box::new(a4));
        for v in vec.iter() {
            v.get_details();
            //  print_type_of(v);
    
            println!("Key : {:?}", v.get_key());
            println!("Value : {:?}", v.get_value());
        }
    }
    
    
    

    Playground