How can i store different type that impl a same trait in HashSet

im trying to make some thing like Observer Pattern, i encounterd a problem, i want to use

Box<dyn Observer>

to store different type in HashSet :

    pub struct Stock {
        observer_list: HashSet<Box<dyn Observer>>,
        price: i32,
    }

but when i try to insert something into observer_list, something went wrong:

error[E0599]: the method `insert` exists for struct `HashSet<Box<dyn Observer>>`, but its trait bounds were not satisfied
   --> src\lib.rs:68:32
    |
68  |               self.observer_list.insert()
    |                                  ^^^^^^
    |
   ::: C:\Users\ASUS\.rustup\toolchains\stable-x86_64-pc-windows-gnu\lib/rustlib/src/rust\library\alloc\src\boxed.rs:195:1
    |
195 | / pub struct Box<
196 | |     T: ?Sized,
197 | |     #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global,
198 | | >(Unique<T>, A);
    | |_- doesn't satisfy `Box<(dyn Observer + 'static)>: Eq`, `Box<(dyn Observer + 'static)>: Hash` or `Box<(dyn Observer + 'static)>: PartialEq`
    |
    = note: the following trait bounds were not satisfied:
            `Box<(dyn Observer + 'static)>: Eq`
            `Box<(dyn Observer + 'static)>: PartialEq`
            which is required by `Box<(dyn Observer + 'static)>: Eq`
            `Box<(dyn Observer + 'static)>: Hash`

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0599`.
error: could not compile `rs_test_new` (lib) due to 2 previous errors

and i have tried hard to impl those trait but in vain. Here's the code:


pub mod observer_pattern {
    use std::collections::HashSet;

    trait Observer{
        fn update(&self);
    }


    trait ObserverManager
    {
        fn attach(&mut self, things: Box<impl Observer>) -> bool;
        fn detach(&mut self, things: Box<impl Observer>) -> bool;
        fn notify(&self);
    }

    pub struct Monitor<'a>(&'a Stock);

    impl<'a> Observer for Monitor<'a>
    {
        fn update(&self) {
            self.print(self.0.get_price())
        }
    }

    impl<'a> Monitor<'a>
    {
        pub fn new(thing: &mut Stock) -> Monitor {
            Monitor(thing)
        }
        fn print(&self, i: i32) {
            println!("Monitor: {}", i);
        }
    }

    pub struct BillBoard<'a>(&'a Stock);

    impl<'a> Observer for BillBoard<'a>
    {
        fn update(&self) {
            self.display(self.0.get_price())
        }
    }

    impl<'a> BillBoard<'a> {
        pub fn new(thing: &Stock) -> BillBoard {
            BillBoard(thing)
        }
        fn display(&self, i: i32) {
            println!("BillBoard: {i}");
        }
    }

    pub struct Stock {
        observer_list: HashSet<Box<dyn Observer>>,
        price: i32,
    }

    impl ObserverManager for Stock
    {
        fn attach(&mut self, things: Box<impl Observer>) -> bool{
            self.observer_list.insert()
        }
        fn detach(&mut self, things: Box<impl Observer>) -> bool{
            todo!()
        }
        fn notify(&self) {
            todo!()
        }
    }

    impl Stock {
        pub fn new(price: i32) -> Stock {
            Stock { price, observer_list: HashSet::new() }
        }
        pub fn get_price(&self) -> i32 {
            self.price
        }
        pub fn set_price(&mut self, price: i32) {
            self.price = price;
            // self.notify();
        }
    }
}

and here's the main function:

use rs_test_new::observer_pattern::*;
fn main() {
    let mut stock = Stock::new(55);
    let mo = Monitor::new(&stock);
    let bb = BillBoard::new(&stock);
    stock.set_price(20);

}

i wish when use method set_price everyone in the observer_list will print a line to prove that it did receive the changes.
please help me , i will really appriciate it !!

Unlike some other languages like Python, Rust does not have an equality operation that can be used to compare values of potentially different types. So, the only way to have this work would be to make your Observer trait able to return some kind of comparison key. But, if you're going to do that, then you might as well pass the key separately, so do that. Or even better, have the observer list choose and return a key.

struct Key(u64);

trait ObserverManager {
    fn attach(&mut self, observer: Box<dyn Observer>) -> Key;
    fn detach(&mut self, key: Key) -> bool;
}

pub struct Stock {
    observer_list: HashMap<Key, Box<dyn Observer>>,
    ...
2 Likes

Depends on how to handle different values for the same type:

  • only store distinct types by discarding/replacing a new value for the same (exsiting) type: Rust Playground
  • store distinct types by tolerating new values for the same type: Rust Playground
1 Like

Since you're using Box<dyn Observer>, which is actually Box<dyn Observer + 'static>, you may be fine adding a 'static bound. In which case, you could use dyn Any as a supertrait bound to enable downcasting and make dyn Obverser comparable for equality, and also hashable.

But now that I try to apply that to your actual structs, you probably don't want the 'static bound due to your borrowing types like Monitor<'_>. Box<dyn Observer> would have given you problems. (So will TypeId.)

So the "return a key" approach is probably a better fit for you. You'll probably have to do some thinking about what the key should be. It can't just be naively &Stock either, because HashSet<_> doesn't implement Hash.

Alternatively maybe you need Arc<Stock> or Rc<Stock> instead of &'_ Stock.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.