如何在Rust中实现观察者模式? [英] How can I implement the observer pattern in Rust?

查看:197
本文介绍了如何在Rust中实现观察者模式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an observable collection and an observer. I want the observer to be a trait implementation of trait Observer. The observable object should be able to notify each observer when some event occurs. This should explain my intentions:

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);
        }
    }
}

(游乐场)

我得到了错误:

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 ++中有这样的代码,但是我不知道如何在Rust中实现观察者模式.我相信我的问题是在可观察对象内部存储了对观察者对象的引用.

This is just a mock-up of what I want to do. I have code like this in Java, Python, and C++, but I don't know how to implement the observer pattern in Rust. I believe my problem is in storing a reference to observer objects inside observable objects.

推荐答案

根据实现的选择,观察者模式可能会带来所有权挑战.

The Observer pattern, depending on implementation choices, may pose an ownership challenge.

在垃圾回收语言中,通常有Observable引用Observer(以通知它)和Observer引用Observable(以注销自身)...这带来了一些挑战就所有权(谁比谁活了?)而言,这就是整个注销时的通知".

In garbage collected languages it is typical to have the Observable referring to the Observer (to notify it) and the Observer referring to the Observable (to unregister itself)... this causes some challenges in terms of ownership (who outlives whom?) and there is this whole "notification on un-registering" thing.

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

In Rust (and C++), I advise avoiding cycles.

简单的解决方案

ObservableObserver具有不同的生存期,没有一个拥有另一个,或者预计将超过另一个.

The Observable and Observer have distinct lifetimes, none owning the other or being expected to outlive the other.

use std::rc::Weak;

struct Event;

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

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

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

The key is to allocate the Observer into a Rc and then hand over Weak (weak references) to the Observable.

如果需要在Event上修改Observer,则它要么需要内部可变性,要么需要包装成

If the Observer needs be modified on the Event, then either it needs internal mutability or it needs to be wrapped into a RefCell (passing Weak<RefCell<dyn Observer>> to the Observable).

通知时,Observable会定期意识到存在弱引用(Observer消失了),然后可以懒惰地删除这些引用.

When notifying, the Observable will regularly realize that there are dead weak-references (the Observer has disappeared), it can remove those then, lazily.

还有其他解决方案,例如使用Broker(非常类似于事件循环),从推送模式转换为拉动模式(即(1)生成所有事件,(2)处理所有事件),但是这些解决方案会分开与传统的观察者模式有点不同,并且具有不同的优点/缺点,因此在这里我不会尝试将它们全部对待.

There are other solutions, such as using a Broker (quite similar to an event loop), moving from push mode to pull mode (i.e. (1) generate all events, (2) treat all of them), however these depart a bit from the traditional Observer Pattern and have different pluses/minuses so I will not attempt to treat them all here.

这篇关于如何在Rust中实现观察者模式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆