rusthashmaplifetimeheterogeneous

Heterogeneous collection as a member of a class in Rust


I am new to Rust, and does not fully understand lifetime, so probably, that is why I can't solv the following issue. I need a solution in which a class has a heterogeneous HashMap containing different objects derived from the same trait.

I have to be able to extend an object with some (multiple) functionality dinamically. Other solutions are also welcome. Adding functionality to the class in compile time could also work, but adding functionality directly to the main class not.

use std::collections::HashMap;

trait DoerTrait {
    fn do_something( & self, a : u8, b : u8 ) -> u8;
}

struct MyDoer<'a> {
}

impl DoerTrait for MyDoer<'a> {
    fn do_something( & self, a : u8, b : u8 ) -> u8 {
        return a + b;
    }
}

struct MyMain<'a> {
    doers : HashMap<u8,&'a dyn DoerTrait>,
}

impl<'a> MyMain<'a> {
    fn new() -> Self {
        Self {
            doers : HashMap::new()
        }
    }

    fn add_doer( &mut self, id : u8, doer : & dyn DoerTrait ) {
        self.doers.insert( id, doer );
    }

    fn do_something( & self, id : u8 ) {
        match self.doers.get( &id ) {
            Some( doer ) => {
                println!( "{}", doer(19,26) );
            }
            None => {
                println!( "Doer not specified!" );
            }
        }
    }
}

fn main() {
    let mut mymain = MyMain::new();
    let mydoer = MyDoer{};
    mymain.add_doer( 42, &mydoer );
    mymain.do_something( 42 );
}

Solution

  • Not too sure what issue you have, once MyDoer has been stripped of its incorrect (unnecessary) lifetime and the lifetime has correctly been declared on impl MyMain, the compiler directly points to the parameter of add_doer not matching (after which it points out that doer in do_something is not a function):

    use std::collections::HashMap;
    
    trait DoerTrait {
        fn do_something(&self, a: u8, b: u8) -> u8;
    }
    
    struct MyDoer;
    
    impl DoerTrait for MyDoer {
        fn do_something(&self, a: u8, b: u8) -> u8 {
            return a + b;
        }
    }
    
    struct MyMain<'a> {
        doers: HashMap<u8, &'a dyn DoerTrait>,
    }
    
    impl<'a> MyMain<'a> {
        fn new() -> Self {
            Self {
                doers: HashMap::new(),
            }
        }
    
        fn add_doer(&mut self, id: u8, doer: &'a dyn DoerTrait) {
            self.doers.insert(id, doer);
        }
    
        fn do_something(&self, id: u8) {
            match self.doers.get(&id) {
                Some(doer) => {
                    println!("{}", doer.do_something(19, 26));
                }
                None => {
                    println!("Doer not specified!");
                }
            }
        }
    }
    
    fn main() {
        let mut mymain = MyMain::new();
        let mydoer = MyDoer {};
        mymain.add_doer(42, &mydoer);
        mymain.do_something(42);
    }