Expand description
§flo_binding
, a data-driven binding library
flo_binding
provides a way to create ‘data bindings’, which are a way to store
and share state between different parts of an application. Applications designed
around these data structures are sometimes referred to as ‘reactive’ applications.
A basic binding can be created using the bind()
function:
let binding = bind(1);
This can be updated by calling set()
and retrieved using get()
:
let value = binding.get(); // == 1
binding.set(2);
let value = binding.get(); // == 2
Cloning a binding will share the state:
let another_binding = binding.clone();
another_binding.set(3);
let value = another_binding.get(); // == 3
let value = binding.get(); // Also == 3
There are two main ways to be notified when a binding has changed. The
when_changed()
function provides a way to call a function whenever a binding
has changed since it was last read, and the follow()
function will return a
Stream of the most recent value attached to the binding. The streaming approach
is the most flexible.
let binding = bind(1);
let mut event_lifetime = binding.when_changed(notify(|| { println!("Binding changed"); }));
let mut binding_stream = follow(binding.clone());
let value = binding_stream.next().await; // == 1
binding.set(2); // Prints 'Binding changed'
binding.set(3); // Changed flag is set, so prints nothing
let value = binding.get(); // == 3
binding.set(4); // Prints 'Binding changed' again
let value = binding_stream.next().await; // == 4 (stream does not return intermediate values)
let value = binding_stream.next().await; // Blocks until something elsewhere updates the binding
event_lifetime.done();
binding.set(5); // Prints nothing as the 'when_changed' event has been deregistered
The inverse of follow()
is bind_stream()
, which creates a bindings that is kept
up to date with whatever the last value received from a stream is:
let binding = bind(1);
let binding_from_stream = bind_stream(follow(binding.clone()), 1, |_old_value, new_value| new_value);
let value = binding.get(); // == 1
let value = binding_from_stream.get(); // == 1
binding.set(2);
let value = binding_from_stream.get(); // == 2
A stream binding like this is read-only, but is a good way to convert any stream of state values into a value representing a static state. It implements all of the other binding operations.
Another important type of binding is the computed()
binding, which makes it possible
to create a binding that derives a value from other bindings. Computed bindings
automatically monitor any bindings that were captured for changes, so they can be
follow
ed or when_change
d as with any other binding:
let binding = bind(1);
let binding_copy = binding.clone();
let one_more = computed(move || binding_copy.get() + 1);
let event_lifetime = one_more.when_changed(notify(|| println!("Computed binding changed")));
let value = one_more.get(); // == 2 (1 + 1)
binding.set(2); // Prints 'Computed binding changed'
binding.set(3); // Computed binding has not been read since the last notification so prints nothing
let value = one_more.get(); // == 4 (3 + 1)
For collections of data, flo_binding
uses the concept of a ‘rope binding’. The
general rope data type is provided by the flo_rope
crate. These bindings send differences rather than their full state when streaming and
are internally represented by a data structure that can perform deletions and insertions
efficiently. Unlike the traditional concept of a rope, they aren’t limited to editing
strings, and can annotate their contents with attributes, which makes them suitable for
representing sequences of any kind, or sequences of rich text annotated with style
information.
let mutable_rope = RopeBindingMut::<usize, ()>::new();
let rope_copy = RopeBinding::from_stream(mutable_rope.follow_changes());
let mut rope_stream = rope_copy.follow_changes();
mutable_rope.replace(0..0, vec![1, 2, 3, 4]);
let next = rope_stream.next().await; // == RopeAction::Replace(0..0, vec![1,2,3,4]))
let rope_len = rope_copy.len(); // == 4
let rope_content = rope_copy.read_cells(0..4).collect::<Vec<_>>(); // == vec![1, 2, 3, 4]
The flo_rope
library provides some extra functionality - for example, a way to create the
RopeAction
s for a rope by using a diff algorithm on a changing sequence instead of just
reporting the changes as they arrive.
§Companion libraries
Aside from flo_rope
, the desync
crate provides a
novel approach to asynchronous code, including pipe operations that work very well with
the streamed updates from the follow()
function.
The flo_stream
provides a pubsub system that
provides more flexible ways for distributing state through streams.
flo_scene
is a runtime for building
complex systems out of entities that exchange messages with each other. It uses
flo_binding
as an ergonomic way to exchange state information.
Modules§
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 withBindRef::from(binding)
. - Binding
- Represents a thread-safe, sharable binding
- Computed
Binding - Represents a binding to a value that is computed by a function
- Follow
Stream - Stream that follows the values of a binding
- Rope
Binding - A rope binding binds a vector of cells and attributes
- Rope
Binding Mut - A rope binding binds a vector of cells and attributes
- Rope
Stream - A rope stream monitors a rope binding, and supplies them as a stream so they can be mirrored elsewhere
- Stream
Binding - Binding that represents the result of binding a stream to a value
Traits§
- Bound
- Trait implemented by something that is bound to a value
- Bound
Rope - Trait implemented by types that represent a rope binding
- Bound
Rope Ext - Extension methods that can be applied to any bound rope
- Changeable
- Trait implemented by items that can notify something when they’re changed
- Mutable
Bound - 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.
- With
Bound - Trait implemented by something that is bound to a value
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