Use actix as replacement for observer pattern?

I'm creating a workspace model for a desktop application.

The workspace will have a file tree, open documents with mutable data, live queries over the documents and file tree, etc. At this point I've used rust to build the file tree component. Now I want to start building up the other parts, and I need to decide how everything communicates. For example live queries need to know about changes in the file tree and document state. Views need to know about changes to live queries. Other views need to know about changes to document models, etc.

Previous to rust I would solve this problem by having each model maintain and notify a list of observers. Or maybe use a single notification center (such as NSNotificationCenter) to manager the subscriptions and send the events. But in rust these implementations seem a little messy to implement... dealing with questions of ownership and mutability. And generally no one seems to use or updates the crates that I've found that implement this pattern. Anyway, I'm looking for suggestions on best way to solve the problem in rust.

With that in mind I've been reading about Actix (not the web part, just the actor model) and from a distance it's appealing. It seems to make all this communication more structured, decoupled, and it's popular. At the same time it's a big system to buy into, maybe it's overkill for what I'm trying to do.

So my questions:

  1. Would you consider Actix an appropriate solution for managing communication between objects in the workspace model of a desktop application?

  2. If not can you suggest an alternative approach that's better than a default implementation of the observer pattern?

Thanks,
Jesse

I wrote a crate you might be interested in: flo_binding provides a way to store state, watch it for changes and update it from an event source. It's compatible with a design based around Actix as it supplies everything as streams, but Actix isn't required.

It's part of FlowBetween, an animation tool I'm working on, so it's currently maintained but flo_binding's API is pretty straightforward so it's not needed a lot of updates. I've been meaning to split it into its own repo for a while now, like I recently did with flo_curves as it's pretty independently useful. Here's the summary of how it works:

  • Call bind(x) to create a binding with value x. A binding is essentially equivalent to Arc<Mutex<X>>, except you can ask for notifications of any changes.
  • Call computed(...) to create bindings that calculate their value from other bindings. These automatically wire up their events so they save on a lot of boilerplate code (as bindings are references, you can clone them to share them between a computed binding and other parts of the app).
  • Call follow(binding) to create a stream of updates from a binding (normal or computed). This can be used to update a UI.
  • Call bind_stream(event_stream, x, |last_val, new_val| { .... }) to create a binding that updates from a stream of events. This can be used to update the state based on actions that occur in the UI.

There's also a more traditional callback function method for receiving notifications, but streams make managing lifetimes much more intuitive and can be passed from place to place without needing to expose their source (which is great for eliminating the web of dependencies that UI applications typically start to grow).

For creating notification center type APIs, there's a companion library, flo_stream which provides a way to publish a single stream to multiple subscribers.

1 Like

Thanks, I'm taking a look now.