[−][src]Trait mode::Transition
Trait that defines the call signature for a function that can switch an Automaton
from one Mode
(A
) to
another.
State machines, by definition, are systems that possess multiple states that can be switched in or out in order to
change the behavior of the system. Automaton
s are no exception. Eventually, the Automaton
will need to switch
Mode
s in order to change its behavior.
Rather than requiring the owner of the Automaton
to check the status of the current Mode
periodically and swap
in a new state from outside the object when appropriate, the Automaton
instead delegates this responsibility to
the current Mode
via the perform_transitions()
function. Each time this function is called, the Automaton
calls get_transition()
on the current Mode
to determine whether it is ready to transition to another state. To
do this, the Mode
may return a callback function of the form FnOnce(A) -> B
, where A
is the type of the
currently active Mode
and B
is another implementation of Mode
with the same Base
type. The Transition
trait is automatically implemented on any closure of this form, so that get_transitions()
can return a boxed
closure instead of some wrapper type.
NOTE: Although there is rarely a need to do this, it is perfectly valid for a Transition
function to return
the input Mode
as a result. This will result in the currently active Mode
remaining active, even after the
Transition
function has been called.
Usage
use mode::*; impl<'a> Mode<'a> for SomeMode { // ... fn get_transition(&mut self) -> Option<TransitionBox<'a, Self>> { // ... if some_condition { // Returning a Transition function will cause the Automaton to switch the // current Mode to whatever new Mode is produced by the callback. Some(Box::new(|previous : Self| { SomeOtherMode })) } else if some_other_condition { // NOTE: The Transition trait allows this function to return closures with // completely different return types, so long as the parameter types match. Some(Box::new(|previous : Self| { YetAnotherMode })) } else { None } // Returning None will keep the current Mode active. } }
Why Transition
functions?
One of the most powerful features of the Transition
system is that, while transitioning, the current Mode
is
moved into the Transition
callback. This allows Mode
B
, being produced, to steal pointers and other state from
Mode
A
, as opposed to allocating more memory for B
, copying over state from A
, and then deallocating A
.
This won't make much of a difference for small Mode
s, but for Mode
s that contain a large amount of state (on the
order of several megabytes or even gigabytes, as is common in UI applications), this can make a huge difference for
both performance and heap memory usage.
Instead of having the get_transition()
function return a callback, the transition system could have been
implemented as a fn do_transition(self) -> B
function on Mode
. (This is a little bit of an oversimplification.)
However, having a callback allows Transition
s to be scheduled in advance and performed later, capturing state from
the calling code, as necessary. This is especially convenient in cases where a Mode
needs to stay active for a
little longer after a Transition
is scheduled, e.g. allowing a Mode
-driven UI system to finish playing a
transition animation or sound effect before swapping in a new Mode
.
Example
use mode::*; struct SomeMode<'a> { queued_transition : Option<TransitionBox<'a, Self>> } impl<'a> MyMode for SomeMode<'a> { fn update(&mut self) { // ... if (some_condition) { // Queue up a transition to be performed later. self.queued_transition = Some(Box::new(|previous| { SomeOtherMode })) } // TODO: Continue updating, animating, etc. } } impl<'a> Mode<'a> for SomeMode<'a> { // ... fn get_transition(&mut self) -> Option<TransitionBox<'a, Self>> { // ... if ready_to_transition && self.queued_transition.is_some() { // When we're finally finished updating, return the queued transition. self.queued_transition.take() } else { None } // Returning None will keep the current Mode active. } }
Required methods
fn invoke(
self: Box<Self>,
mode: A
) -> Box<dyn AnyModeWrapper<'a, Base = A::Base> + 'a>
self: Box<Self>,
mode: A
) -> Box<dyn AnyModeWrapper<'a, Base = A::Base> + 'a>
Calls the Transition
function on the specified Mode
, consuming the Transition
and the mode
and returning
a wrapper around the new Mode
to be swapped in as active.
NOTE: You should not attempt to implement this function yourself, as the return type makes use of a private
trait (AnyModeWrapper
). Ideally, this function would return a Box<Mode>
in order to abstract away all of the
implementation details of the Automaton
. Unfortunately, that isn't possible, in this case, because the Mode
trait cannot be made into an object, and therefore cannot be boxed.
Implementors
impl<'a, T, A, B> Transition<'a, A> for T where
T: FnOnce(A) -> B,
A: 'a + Mode<'a>,
B: 'a + Mode<'a, Base = A::Base>,
[src]
T: FnOnce(A) -> B,
A: 'a + Mode<'a>,
B: 'a + Mode<'a, Base = A::Base>,