How to find out the correspondding `dyn Object` by concrete object?

Shortly, I have a batch of structs implements same trait, and they are upcasted into dyn object and added into a Notifier, and the Notifier will return the Vec<dyn Object> consists of triggered items. But I cannot findout the concrete one.
Any maybe useful, but I cannot modify Trait Foo.

use std::rc::Rc;
trait Source {
   fn alarm(&self);
}

struct Bar;
impl Source for Bar{
    fn alarm(&self) {
        // DO NOTHING
    }
}

struct Notifier{}

impl Notifier {
    fn add_source(&mut self, source: Rc<dyn Source>) {
        todo!()
    }
    
    fn wait(&self) -> Vec<Rc<dyn Source>> {
        todo!()
    }
}

fn main() {
    let (bar1, bar2, bar3) = (Rc::new(Bar{}), Rc::new(Bar{}), Rc::new(Bar{}));
    let notifier = {
        let mut notifier = Notifier{};
        notifier.add_source(bar1.clone());
        notifier.add_source(bar2.clone());
        notifier.add_source(bar3.clone());
        notifier
    };
    let foos = notifier.wait();
    // FIXME:
    foos.into_iter()
        .find(|b| b == &bar1);
}
Compiling playground v0.0.1 (/playground)
error[E0369]: binary operation `==` cannot be applied to type `&Rc<dyn Source>`
  --> src/lib.rs:37:21
   |
37 |         .find(|b| b == &bar1);
   |                   - ^^ ----- &Rc<Bar>
   |                   |
   |                   &Rc<dyn Source>

For more information about this error, try `rustc --explain E0369`.
error: could not compile `playground` due to previous error

Relating this to the code example, you mean you cannot modify the trait Source? If so, can you not modify the Notifier struct either? If there's too many restrictions, you'll quickly be out of luck and the problem can't be solved. Otherwise, please specify your restrictions a bit more clearly, then perhaps adding a method to Source, or using a subtrait in Notifier, either way getting some &dyn Any (or comparable) view of the contained value, would be an approach that should work.


Edit: Only looked at the playground now, that explains what Foo is referring to.

1 Like

using a subtrait in Notifier

well, actually I'm asked not to make the fat pointer fatter. BTW, could u tell more about the 「subtrait in Notifier」? :grinning:

I'm not sure what this means? Fat pointers in Rust are always two raw pointers in size.

1 Like

for this specific use of Rc, there's Rc::ptr_eq(), however CoerceUnsized is only implemented for Rc<T>, not for &Rc<T> (in other words, the coersion consumes the original Rc), so you either manually cocerce the Rc<Bar> into Rc<dyn Source> for the closure to capture, or you take a clone of the Rc on each comparison

// manually cocersion outside the closure
// you can make a clone of bar1 if the concrete type is needed later
let target: Rc<dyn Source> = bar1; // bar1.clone();
foo.into_iter().find(|b| Rc::ptr_eq(b, &target));

// make a clone of bar1 each comparison
foo.into_iter().find(|b| Rc::ptr_eq(b, &bar1.clone());
1 Like

but vtable will grow.

:smiling_face_with_three_hearts: It works for me. Thanks a lot!

Vtables are static data, not per instance, so you must be very tight on storage if you can't increase vtable size.

3 Likes

A typo... wanted to say “subtrait of Notifier”. Something intuitively equivalent to Notifier + Any could be built, let's call it AnyNotifier (for lack of a better name), then the struct could work ist Box<dyn AnyNotifier> but still allows viewing as &dyn Any or &dyn Notifier (using the original not-supposed-to-be-modified trait), whenever necessary.

2 Likes

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.