如何将具体的、静态调度的“T: Trait”转换为动态调度的“dyn Trait”?

How do I convert a concrete, statically dispatched `T: Trait` to a dynamically dispatched `dyn Trait`?

在这段代码中,我有一个可观察系统的骨架。关于实现 Observable 和其他解耦模式的文档通常缺少允许访问侦听器的最后一步,同时在通知调用期间也将其设为 &mut,但这正是 RefCell 旨在处理的内容。所以我已经完成了这段代码,但是我在将我的具体 T 放入 &dyn Trait.

中遇到了最后的麻烦
use std::{cell::RefCell, rc::Rc};

pub trait Observer {
    fn notify(&mut self);
}

#[derive(Default)]
pub struct Subject<'s> {
    listeners: Vec<Rc<RefCell<Box<dyn Observer + 's>>>>,
}

pub fn wrap<T>(t: T) -> Rc<RefCell<Box<T>>> {
    Rc::new(RefCell::new(Box::new(t)))
}

impl<'s> Subject<'s> {
    pub fn add_observer(&mut self, observer: Rc<RefCell<Box<dyn Observer + 's>>>) {
        self.listeners.push(observer)
    }

    pub fn notify(&mut self) {
        for listener in &mut self.listeners {
            listener.borrow_mut().notify();
        }
    }
}

#[cfg(test)]
mod test {
    use super::{wrap, Observer, Subject};

    #[derive(Default)]
    pub struct Counter {
        count: usize,
    }

    impl Observer for Counter {
        fn notify(&mut self) {
            self.count += 1;
        }
    }

    #[test]
    fn it_observes() {
        let mut subject = Subject::default();
        let counter = wrap(Counter::default());

        subject.add_observer(counter); // mismatched types

        for i in 1..5 {
            subject.notify();
            subject.notify();

            assert_eq!(counter.borrow().count, i * 2);
        }
    }
}

完整的错误是

error[E0308]: mismatched types
  --> src/observer.rs:48:30
   |
48 |         subject.add_observer(counter);
   |                              ^^^^^^^ expected trait object `dyn Observer`, found struct `Counter`
   |
   = note: expected struct `Rc<RefCell<Box<(dyn Observer + 'static)>>>`
              found struct `Rc<RefCell<Box<Counter>>>

我已经看到(看起来像)这种模式在许多情况下使用,但我不知道我遗漏了什么或做了什么不同的事情。

如何让 T: Trait 脱离静态调度并进入动态调度?

您可以在 wrap 函数之外进行装箱,并使用适当的框类型对计数器本身进行装箱:


pub fn wrap<T>(t: T) -> Rc<RefCell<T>> {
    Rc::new(RefCell::new(t))
}
...
let counter: Box<dyn Observer> = Box::new(Counter::default());
let counter = wrap(counter);

Playground

顺便说一句,一旦你有了动态调度,你就有了一个 dyn Observer 所以你无法访问 Counter 本身。您将必须再次取得它的所有权,downcast 指向具体类型的指针。