[][src]Crate flo_binding

flo_binding, a data-driven binding library

flo_binding is a library of types intended to help store state in interactive applications. A binding is a mutable value that can notify observers when it changes, or can supply its values as a stream. This is intended to make it easy to build 'data-driven' applications, where updates to model state automatically drive updates to the rest of the application.

This method of propagating state is sometimes called 'reactive programming', and has also been called 'parametric programming' in the past: it's most notable for being the methodology behind spreadsheets and CAD applications before its recent rediscovery in web applications. flo_binding prefers the term 'data-driven' as it's a more plain description of how changes are pushed through an application using the library.

The advantage of using a binding library like this over a standard event-driven architecture is that it's not necessary to send events or manually update state when updating the application data model. Bugs are reduced because it's very much harder for a dependent state update to go missing as this library can automatically associate.

There are three main types of binding provided by the flo_binding library:

A normal binding, created by bind(value) can be updated to a new value by calling set(). These are useful for representing state that is directly manipulated by the user.

A computed binding, created by compute(move || { some_func() }). When a computed binding accesses the value of another binding by calling get(), it will automatically monitor it for changes and indicate that it has changed whenever that value changes. This makes it easy to create chains of dependent bindings.

A stream binding, created by bind_stream(stream, initial_value, |old_value, new_value| { /* ... */ });. This makes it possible for a binding to update its values in response to a stream of events, such as might be available from a UI framework.

To make using computed bindings easier to generate, cloning a binding creates a reference to the same piece of state. ie: let a = bind(1); let b = a.clone(); will give a and b a reference to the same value, so a.set(2); b.get() will return 2.

Event-driven architecture

The when_changed() function can be called to register a function that is called whenever a binding's value becomes invalidated. Once invalidated, a binding remains invalidated until it's read with get(), which avoids computing values for computed bindings that are never used. This can be used to integrate with traditional UI frameworks.

Calling when_changed() will return a lifetime object: the event will stop firing when this object is dropped (or when done() is called on it). keep_alive() can be used to keep the event firing for as long as the binding exists if necessary.

The event-driven approach to application design has many disadvantages: the need to manage event lifetimes is one, and another important one is the need to pass the actual bindings around in order to attach events to them, so flo_binding provides an alternative approach.

Stream-driven architecture

A superior alternative to the traditional OO-style event-driven architecture is to take a stream-driven approach. Unlike events, streams manage their own lifetimes and can be combined or split, and it's possible to pass around a stream without also providing access to its source, which is a property that can be exploited to produce a less interdependent application. FlowBetween, the application that flo_binding was developed for is an example of a stream-driven application.

Calling follow(binding) will generate a stream of updates from a binding. This can be used to update part of a user interface directly. It can also be used as an input for bind_stream() to create more complicated update rules than are allowed by a simple computed binding.

The companion library desync provides the pipe() and pipe_in() functions which can be used to schedule events generated by a stream.


let mut number      = bind(1);
let number_clone    = number.clone();
let plusone         = computed(move || number_clone.get() + 1);
let mut lifetime    = plusone.when_changed(notify(|| println!("Changed!")));
println!("{}", plusone.get());  // 2
number.set(2);                  // 'Changed!'
println!("{}", plusone.get());  // 3
number.set(3);                  // Lifetime is done, so no notification
println!("{}", plusone.get());  // 4





A BindRef references another binding without needing to know precisely what kind of binding it is. It is read-only, so mostly useful for passing a binding around, particularly for computed bindings. Create one with BindRef::from(binding).


Represents a thread-safe, sharable binding


Represents a binding to a value that is computed by a function


Stream that follows the values of a binding


Binding that represents the result of binding a stream to a value



Trait implemented by something that is bound to a value


Trait implemented by items that can notify something when they're changed


Trait implemented by something that is bound to a value that can be changed


Trait implemented by items with dependencies that need to be notified when they have changed


Trait implemented by an object that can be released: for example to stop performing an action when it's no longer required.



Creates a simple bound value with the specified initial value


Uses a stream to update a binding


Creates a computed value that tracks bindings accessed during the function call and marks itself as changed when any of these dependencies also change


Creates a stream from a binding


Creates a notifiable reference from a function