This great answer on how to implement the traditional observer pattern in Rust concludes with an interesting alternative idea, namely using a broker pattern instead:
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.
It would be great to get a rough outline of this approach that elaborates these pluses/minuses in a bit more detail.
Here is my attempt at understanding the differences. If I understand it correctly, the conceptual differences between the patterns are roughly:
- The observer pattern is typically synchronous, the routing is many-to-many, and notification is push-based (direct callbacks in a sense).
- The broker pattern is rather asynchronous (queue-based), the routing ins many-to-one-to-many and notification is poll-based (no direct callbacks).
Indeed, the lack of direct callbacks seems to avoid ownership issues, because the broker on first glance doesn't need to hold references of the consumers. However, I don't fully see how the broker would actually manage the queue? Can it work with a single queue, or does it need a queue per consumer?
- Single queue: With a single queue, it becomes unclear when to remove something from the queue, because messages must be kept in the queue until all (possible) consumer have consumed them.
- A queue per consumer: This makes it obvious when to remove something from a particular queue, but seems unpractical, because the messages need to be duplicated into many queues, and if a consumer decides to (temporarily) not consume any messages, its corresponding queue would fill up.
In both cases the broker would again need some knowledge about the existing consumers, which kind of brings back the ownership problem.
I'm probably missing a trick in my reasoning, and any clarification would be highly appreciated.