我有一个可观察的集合和一个观察者。我希望观察者成为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);
}
}
}
我收到错误:
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中实现观察者模式。我相信我的问题是在可观察对象中存储对观察者对象的引用。
答案 0 :(得分:9)
Observer
模式(取决于实施选择)可能会带来所有权挑战。
在垃圾收集语言中,Observable
引用Observer
(通知它)和Observer
引用Observable
(取消注册本身)通常是典型的)......这会在所有权方面带来一些挑战(谁比谁更长?)并且有关于取消注册的全部通知&#34;的事情。
在Rust(和C ++)中,我建议避免循环。
简单解决方案
Observable
和Observer
具有不同的生命周期,没有人拥有另一个或预期会比另一个人活得更久。
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>>
传递给{ {1}})。
通知时,Observable
会经常意识到存在死亡的弱引用(Observable
已经消失),它可以随意删除它们。
还有其他解决方案,例如使用Broker(非常类似于事件循环),从推模式转换到拉模式(即(1)生成所有事件,(2)处理所有事件),但这些解决方案有点来自传统的观察者模式,并且有不同的优点/缺点,所以我不会试图在这里对待它们。
答案 1 :(得分:8)
我使用了回调函数。它简单而强大,没有终身问题或类型擦除。
我尝试了Weak<dyn Observer>
,但是
Rc
pub struct Notifier<E> {
subscribers: Vec<Box<dyn Fn(&E)>>,
}
impl<E> Notifier<E> {
pub fn new() -> Notifier<E> {
Notifier {
subscribers: Vec::new(),
}
}
pub fn register<F>(&mut self, callback: F)
where
F: 'static + Fn(&E),
{
self.subscribers.push(Box::new(callback));
}
pub fn notify(&self, event: E) {
for callback in &self.subscribers {
callback(&event);
}
}
}
答案 2 :(得分:5)
这是我基于这个问题的答案和许多痛苦和苦难的实施。我使用弱引用来存储观察者和RefCell
以便能够调用可变notify()
。
我使用Arc
因为我的侦听器可以从任何线程调用。如果您使用的是单个帖子,则可以使用Rc
。
每次调用dispatch()
时,它都会检查是否有任何弱引用的侦听器已消失。如果有,它将清理监听器列表。
pub enum Event {} // You make Event hold anything you want to fire
pub trait Listener {
fn notify(&mut self, event: &Event);
}
pub trait Dispatchable<T>
where T: Listener
{
fn register_listener(&mut self, listener: Arc<RefCell<T>>);
}
pub struct Dispatcher<T>
where T: Listener
{
/// A list of synchronous weak refs to listeners
listeners: Vec<Weak<RefCell<T>>>,
}
impl<T> Dispatchable<T> for Dispatcher<T>
where T: Listener
{
/// Registers a new listener
fn register_listener(&mut self, listener: Arc<RefCell<T>>) {
self.listeners.push(Arc::downgrade(&listener));
}
}
impl<T> Dispatcher<T>
where T: Listener
{
pub fn new() -> Dispatcher<T> {
Dispatcher { listeners: Vec::new() }
}
pub fn num_listeners(&self) -> usize {
self.listeners.len()
}
pub fn dispatch(&mut self, event: Event) {
let mut cleanup = false;
// Call the listeners
for l in self.listeners.iter() {
if let Some(mut listener_rc) = l.upgrade() {
let mut listener = listener_rc.borrow_mut();
listener.notify(&event);
} else {
println!("Cannot get listener, cleanup necessary");
cleanup = true;
}
}
// If there were invalid weak refs, clean up the list
if cleanup {
println!("Dispatcher is cleaning up weak refs");
self.listeners.retain(|ref l| {
// Only retain valid weak refs
let got_ref = l.clone().upgrade();
match got_ref {
None => false,
_ => true,
}
});
}
}
}
这是一个练习它的单元测试代码段。
测试来自卡片游戏库,我的Event
枚举有FlopDealt
和GameFinished
个变体。测试创建并注册我的监听器,并确保在调度FlopDealt
时调用它。范围部分是这样我可以在侦听器超出范围后测试弱引用行为。我触发了另一个事件并计算了侦听器的数量,以确保列表被清除。
use std::time::Instant;
#[derive(Debug)]
pub enum Event {
FlopDealt,
GameFinished { ended: Instant },
}
struct MyListener {
pub flop_dealt: bool,
}
impl Listener for MyListener {
fn notify(&mut self, event: &Event) {
println!("Notify called with {:?}", event);
if let Event::FlopDealt = event {
println!("Flop dealt");
self.flop_dealt = true;
}
}
}
#[test]
fn events_register() {
let mut d: Dispatcher<MyListener> = Dispatcher::new();
{
let listener_rc = Arc::new(RefCell::new(MyListener { flop_dealt: false }));
d.register_listener(listener_rc.clone());
d.dispatch(Event::FlopDealt);
let flop_dealt = listener_rc.borrow().flop_dealt;
println!("Flop was {}dealt", if flop_dealt { "" } else { "not " });
assert_eq!(flop_dealt, true);
assert_eq!(d.num_listeners(), 1);
}
// Listener should disappear
d.dispatch(Event::GameFinished {
ended: Instant::now(),
});
assert_eq!(d.num_listeners(), 0);
}
答案 3 :(得分:0)
如果您仍然对此感兴趣,我写了this答案:
我尝试了this的方式,它对我来说很好用,就像这样简单:
struct
Listeners
,Extensions
,Emitter
将Extensions
选项添加到Self::Fn<Listener>
下面是我在playground中使用的相同代码,我只是在锈蚀forum中解决了它:
// 1. Define your object
//#[derive(Debug)]
pub struct Counter {
count: i32,
}
// 2. (Optional), if do not want to use `#[derive(Debug)]`
// you can define your own debug/release format
impl std::fmt::Debug for Counter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Counter `count` is: {}", self.count)
}
}
// 3. Define your Listeners trait
trait EventListener {
fn on_squared() {
println!("Counter squared")
}
fn on_increased(amount: i32) {
println!("Counter increased by {}", amount)
}
fn on_decreased(self, amount: i32);
}
// 4. Implement your Listeners trait to your object
impl EventListener for Counter {
fn on_decreased(self, amount: i32) {
println!("Counter reduced from {} to {}", &self.count, &self.count - amount)
}
}
// 5. (Recommended), Define your standard functions/Extensions/Emitters
// trait signatures
trait EventEmitter {
fn square(&mut self);
fn increase(&mut self, amount: i32);
fn decrease(&mut self, amount: i32);
fn change_by(&mut self, amount: i32);
}
// 6. Implement your standard functions/Extensions/Emitters trait to your object
impl EventEmitter for Counter {
fn square(&mut self) {
self.count = self.count.pow(2);
Self::on_squared(); // This is Event Emitter, calling the Listner
}
fn increase(&mut self, amount: i32) {
self.count = self.count + amount;
Self::on_increased(amount); // This is Event Emitter, calling the Listner
}
fn decrease(&mut self, amount: i32) {
let initial_value = self.count;
self.count = self.count - amount;
Self::on_decreased(Self {count: initial_value}, amount); // This is Event Emitter, calling the Listner
}
fn change_by(&mut self, amount: i32) {
let initial_value = self.count;
self.count = self.count + amount;
match amount {
x if x > 0 => Self::on_increased(amount), // This is Event Emitter, calling the Listner
x if x < 0 => Self::on_decreased(Self {count: initial_value}, // This is Event Emitter, calling the Listneramount.abs()),
_ => println!("No changes")
}
}
}
// 7. Build your main function
fn main() {
let mut x = Counter { count: 5 };
println!("Counter started at: {:#?}", x.count);
x.square(); // Call the extension, which will automatically trigger the listner
println!("{:?}", x);
x.increase(3);
println!("{:?}", x);
x.decrease(2);
println!("{:?}", x);
x.change_by(-1);
println!("{:?}", x);
}
并获得以下输出:
Counter started at: 5
Counter squared
Counter `count` is: 25
Counter increased by 3
Counter `count` is: 28
Counter reduced from 28 to 26
Counter `count` is: 26
Counter reduced from 26 to 25
Counter `count` is: 25
答案 4 :(得分:0)
防锈设计模式https://github.com/lpxxn/rust-design-pattern
trait IObserver {
fn update(&self);
}
trait ISubject<'a, T: IObserver> {
fn attach(&mut self, observer: &'a T);
fn detach(&mut self, observer: &'a T);
fn notify_observers(&self);
}
struct Subject<'a, T: IObserver> {
observers: Vec<&'a T>,
}
impl<'a, T: IObserver + PartialEq> Subject<'a, T> {
fn new() -> Subject<'a, T> {
Subject {
observers: Vec::new(),
}
}
}
impl<'a, T: IObserver + PartialEq> ISubject<'a, T> for Subject<'a, T> {
fn attach(&mut self, observer: &'a T) {
self.observers.push(observer);
}
fn detach(&mut self, observer: &'a T) {
if let Some(idx) = self.observers.iter().position(|x| *x == observer) {
self.observers.remove(idx);
}
}
fn notify_observers(&self) {
for item in self.observers.iter() {
item.update();
}
}
}
#[derive(PartialEq)]
struct ConcreteObserver {
id: i32,
}
impl IObserver for ConcreteObserver {
fn update(&self) {
println!("Observer id:{} received event!", self.id);
}
}
fn main() {
let mut subject = Subject::new();
let observer_a = ConcreteObserver { id: 1 };
let observer_b = ConcreteObserver { id: 2 };
subject.attach(&observer_a);
subject.attach(&observer_b);
subject.notify_observers();
subject.detach(&observer_b);
subject.notify_observers();
}
输出
Observer id:1 received event!
Observer id:2 received event!
Observer id:1 received event!