Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 698
Vinay Sawant Avatar asked Sep 18 '25 16:09

Vinay Sawant


1 Answers

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

like image 95
Netwave Avatar answered Sep 21 '25 11:09

Netwave