Crate flo_binding[][src]

Bindings

This library provides a mechansim for designing applications with a data-driven control flow. This is essentially the paradigm found in spreadsheets, where a formula will update automatically if its dependencies update. This is a convenient way to express how a user interface updates, as it eliminates the need to describe and manage events.

A simple binding can be created using let binding = bind(X). This creates a simple mutable binding that can be updated using binding.set(Y) or retrieved using binding.get().

This value can be monitored using when_changed, as in let lifetime = binding.when_changed(notify(|| println!("Changed!"))). The lifetime value returned can be used to stop the notifications from firing. Unless lifetime.keep_alive() is called, it will also stop the notifications once it goes out of scope.

So far, this is very similar to traditional observables. What makes bindings different is the idea that they can be computed as well. Here's an example:

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
lifetime.done();
 
number.set(3);                  // Lifetime is done, so no notification
println!("{}", plusone.get());  // 4

Computed values can be as complicated as necessary, and will notify the specified function whenever their value changes.

Cloning a binding creates a new binding that references the same location. This makes it easier to pass bindings into closures (though still a little awkward as Rust doesn't really have a shorthand way of doing this for philosophical reasons). They're similar in behaviour to an Arc<Mutex<X>> object in this respect (and never really immutable given that cloning them creates a mutable object)

Computed bindings as demonstrated above are 'lazy'. They don't know their own value until they have been evaluated after a change (and start in this 'uncertain' state), so they will not notify of value changes until they have been read at least once and will not notify again until they have been read again. Reading the value from within the notification is possible but in general the idea is to queue up an update for later: being lazy in this way prevents repeated computations of intermediate values when many values are being updated. Knock-on effects are all accounted for, so if a future update is queued, it won't trigger further notifications.

Modules

binding_context

Structs

BindRef

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).

Binding

Represents a thread-safe, sharable binding

ComputedBinding

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

FollowStream

Stream that follows the values of a binding

StreamBinding

Binding that represents the result of

Traits

Bound

Trait implemented by something that is bound to a value

Changeable

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

MutableBound

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

Notifiable

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

Releasable

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

Functions

bind

Creates a simple bound value with the specified initial value

bind_stream

Uses a stream to update a binding

computed

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

follow

Creates a stream from a binding

notify

Creates a notifiable reference from a function