我有一个可观察的集合和一个观察者.我希望观察者是trait Observer的特征实现.当某个事件发生时,可观察对象应该能够通知每个观察者.这应该可以解释我的意图:

struct A {
    observables: Vec<Observable>,
}

impl A {
    fn new() -> A {
        A {
            observables: vec![],
        }
    }
}

trait Observer {
    fn event(&mut self, _: &String);
}

impl Observer for A {
    fn event(&mut self, ev: &String) {
        println!("Got event from observable: {}", ev);
    }
}

struct Observable {
    observers: Vec<dyn Observer>, // How to contain references to observers? (this line is invalid)
}

impl Observable {
    fn new() -> Observable {
        Observable {
            observers: Vec::new(),
        }
    }

    fn add_observer(&mut self, o: &dyn Observer) {
        // incorrect line too
        self.observers.push(o);
    }

    fn remove_observer(&mut self, o: &dyn Observer) {
        // incorrect line too
        self.observers.remove(o);
    }

    fn notify_observers(&self, ev: &String) {
        for o in &mut self.observers {
            o.event(ev);
        }
    }
}

(Playground)

我得到了一个错误:

error[E0277]: the size for values of type `(dyn Observer + 'static)` cannot be known at compilation time
  --> src/lib.rs:24:5
   |
24 |     observers: Vec<dyn Observer>, // How to contain references to observers?
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `(dyn Observer + 'static)`
   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
   = note: required by `std::vec::Vec`

这只是我想做的一个模型.我在java、python和C++中有这样的代码,但是我不知道如何在鲁斯特中实现观察者模式.我认为我的问题是在可观察对象中存储对观察对象的引用.

推荐答案

观察者模式(取决于实现 Select )可能会带来所有权挑战.

在垃圾收集语言中,通常Observable指的是Observer(通知它),Observer指的是Observable(注销自己)...这在所有权方面带来了一些挑战(谁活得比谁长?)还有整个"取消注册通知"的事情.

在 rust (和C++)中,我建议避免循环.


Simple solution

ObservableObserver的生命周期 截然不同,没有一个拥有另一个,也没有一个比另一个长寿.

use std::rc::Weak;

struct Event;

trait Observable {
    fn register(&mut self, observer: Weak<dyn Observer>);
}

trait Observer {
    fn notify(&self, event: &Event);
}

关键是将Observer分配到Rc,然后将Weak(弱引用)交给Observable.

如果Observer需要在Event上修改,那么它要么需要内部可变性,要么需要包装成RefCell(将Weak<RefCell<dyn Observer>>传递给Observable).

当发出通知时,Observable会定期意识到存在非常弱的引用(Observer已经消失),然后它可以懒洋洋地删除这些引用.


还有其他解决方案,例如使用代理(非常类似于事件循环),从推模式移动到拉模式(即:(1)生成所有事件,(2)处理所有事件),但是这些都有点偏离传统的观察者模式,有不同的优缺点,所以我不会在这里try 处理所有事件.

Rust相关问答推荐

如何初始化match声明中的两个变量而不会激怒borrow 判断器?

收集RangeInclusive T到Vec T<><>

如何最好地并行化修改同一Rust向量的多个切片的代码?

当rust中不存在文件或目录时,std::FS::File::Create().unwire()会抛出错误

带扫描的铁 rust 使用滤镜

具有对同一类型的另一个实例的可变引用的

除了调用`waker.wake()`之外,我如何才能确保future 将再次被轮询?

如何定义实现同名但返回类型不同的 struct 的函数

告诉Rust编译器返回值不包含构造函数中提供的引用

避免在Collect()上进行涡鱼类型的涂抹,以产生<;Vec<;_>;,_>;

有没有可能让泛型Rust T总是堆分配的?

如何对一个特征的两个实现进行单元测试?

我可以禁用发布模式的开发依赖功能吗?

在发布中包含 Rust DLL

Rust中的标记特征是什么?

从Rust 的临时文件中创建引用是什么意思?

在 Rust 中使用 `SecTrustSettingsSetTrustSettings` 绑定导致 `errSecInternalComponent`

我的 Axum 处理程序无法编译:未实现 IntoResponse 特征

在 Rust 中如何将值推送到枚举 struct 内的 vec?

没有通用参数的通用返回