[][src]Struct mode::Automaton

pub struct Automaton<'a, Base> where
    Base: ?Sized
{ /* fields omitted */ }

Represents a state machine over a set of Modes that can be referenced via some common interface Base.

The Automaton contains a single, active Mode that represents the current state of the state machine. The current Mode is accessible via the borrow_mode() and borrow_mode_mut() functions, which return a Base reference. Functions and members of the inner Base type can also be accessed directly via the Deref and DerefMut traits. The Automaton provides a perform_transitions() function that should be called at some point in order to allow the current Mode to transition another Mode in, if desired.

See Mode::get_transition() for more details.

The 'a lifetime

Most types in this library include an explicit 'a lifetime, which represents the lifetime of the Automaton wrapping each Mode. In order for a Mode to be used with an Automaton, all references within the Mode must outlive the parent Automaton. Having this lifetime allows for the creation of Modes that store references to objects that outlive the Automaton but are still declared on the stack. (See example below.)

Example

use mode::*;
 
struct IncrementMode<'a> {
    pub number : &'a mut u32,
    pub step : u32,
}
 
impl<'a> IncrementMode<'a> {
    fn update(&mut self) {
        *self.number += self.step
    }
 
    fn get_step(&self) -> u32 { self.step }
}
 
impl<'a> Mode<'a> for IncrementMode<'a> {
    type Base = Self;
    fn as_base(&self) -> &Self::Base { self }
    fn as_base_mut(&mut self) -> &mut Self::Base { self }
    fn get_transition(&mut self) -> Option<TransitionBox<'a, Self>> {
        if self.step > 0 {
            // Transition to another IncrementMode with a lower step amount.
            Some(Box::new(|previous : Self| {
                IncrementMode { number: previous.number, step: previous.step - 1 }
            }))
        }
        else { None } // None means don't transition
    }
}
 
// Create a shared counter and pass it into the Mode.
let mut number : u32 = 0;
 
// NOTE: The Automaton can't outlive our shared counter.
{
    let mut automaton =
        Automaton::with_initial_mode(IncrementMode { number: &mut number, step: 10 });
     
    // NOTE: Automaton implements Deref so that all Base functions can be called
    // through an Automaton reference.
    while automaton.get_step() > 0 {
        // Update the current Mode.
        automaton.update();
     
        // Let the Automaton handle transitions.
        automaton.perform_transitions();
    }
}
 
// Make sure we got the right result.
assert_eq!(number, 55);

The Base parameter

The Base parameter may be either a trait (e.g. Automaton<dyn SomeTrait>) or a concrete type (e.g. Automaton<SomeStructThatImplsMode>). Given a trait, the Automaton will be able to swap between any Modes that implement the trait. However, this means that the Automaton will only allow the inner Mode to be borrowed via a trait reference, implying that only functions defined on the trait will be callable.

By contrast, if given a struct, all functions defined on the inner type will be accessible from outside the Automaton. However, this also implies that the Automaton will only be able to switch between states of the same concrete type.

For more on the Base parameter, see Mode.

Usage

use mode::*;
 
// Use with_initial_mode() to create the Automaton with an initial state.
let mut automaton = Automaton::with_initial_mode(SomeMode);
 
// Functions can be called on the inner Mode through an Automaton reference
// via the Deref and DerefMut traits
automaton.some_fn();
automaton.some_mut_fn();
 
// If you want to be more explicit, use borrow_mode() or borrow_mode_mut();
automaton.borrow_mode().some_fn();
automaton.borrow_mode_mut().some_mut_fn();
 
// Let the Automaton handle transitions.
automaton.perform_transitions();

Methods

impl<'a, Base: ?Sized> Automaton<'a, Base> where
    Base: 'a, 
[src]

pub fn with_initial_mode<M>(initial_mode: M) -> Self where
    M: 'a + Mode<'a, Base = Base>, 
[src]

Creates a new Automaton with the specified initial_mode, which will be the active Mode for the Automaton that is returned.

pub fn perform_transitions(&mut self)[src]

Calls get_transition() on the current Mode to determine whether it wants to transition out. If a Transition is returned, the Transition callback will be called on the current Mode, swapping in whichever Mode it returns as a result.

See Transition and Mode::get_transition() for more details.

pub fn borrow_mode(&self) -> &Base[src]

Returns an immutable reference to the current Mode as a &Self::Base, allowing immutable functions to be called on the inner Mode.

NOTE: Automaton also implements Deref<Target = Base>, allowing all Base members to be accessed via a reference to the Automaton. Hence, you can usually leave the borrow_mode() out and simply treat the Automaton as if it were an object of type Base.

pub fn borrow_mode_mut(&mut self) -> &mut Base[src]

Returns a mutable reference to the current Mode as a &mut Self::Base, allowing mutable functions to be called on the inner Mode.

NOTE: Automaton also implements Deref<Target = Base>, allowing all Base members to be accessed via a reference to the Automaton. Hence, you can usually leave the borrow_mode_mut() out and simply treat the Automaton as if it were an object of type Base.

impl<'a, Base> Automaton<'a, Base> where
    Base: 'a + Mode<'a, Base = Base> + Default
[src]

pub fn new() -> Self[src]

Creates a new Automaton with a default Mode instance as the active Mode.

NOTE: This only applies if Base is a concrete type (e.g. Automaton<SomeStructThatImplsMode>) that implements Default. If Base is a trait type (e.g. Automaton<dyn SomeTraitThatExtendsMode>) or you would otherwise like to specify the initial mode of the created Automaton, use with_initial_mode() instead.

use mode::*;
 
struct ConcreteMode { count : u32 };
 
impl<'a> Mode<'a> for ConcreteMode {
    type Base = Self;
    fn as_base(&self) -> &Self { self }
    fn as_base_mut(&mut self) -> &mut Self { self }
    fn get_transition(&mut self) -> Option<TransitionBox<'a, Self>> {
        // TODO: Logic for transitioning between states goes here.
        Some(Box::new(
            |previous : Self| {
                ConcreteMode { count: previous.count + 1 }
            }))
    }
}
 
impl Default for ConcreteMode {
    fn default() -> Self {
        ConcreteMode { count: 0 }
    }
}
 
// Create an Automaton with a default Mode.
// NOTE: Deref coercion allows us to access the CounterMode's count variable
// through an Automaton reference.
let mut automaton = Automaton::<ConcreteMode>::new();
assert!(automaton.count == 0);
 
// Keep transitioning the current Mode out until we reach the target state
// (i.e. a count of 10).
while automaton.count < 10 {
    automaton.perform_transitions();
}

Trait Implementations

impl<'a, Base> Default for Automaton<'a, Base> where
    Base: 'a + Mode<'a, Base = Base> + Default
[src]

fn default() -> Self[src]

Creates a new Automaton with the default Mode active. This is equivalent to calling Automaton::new().

See note on new() for more on when this function can be used.

impl<'a, Base: ?Sized> DerefMut for Automaton<'a, Base> where
    Base: 'a, 
[src]

fn deref_mut(&mut self) -> &mut Base[src]

Returns a mutable reference to the current Mode as a &mut Self::Base, allowing mutable functions to be called on the inner Mode.

impl<'a, Base: ?Sized> Deref for Automaton<'a, Base> where
    Base: 'a, 
[src]

type Target = Base

The resulting type after dereferencing.

fn deref(&self) -> &Base[src]

Returns an immutable reference to the current Mode as a &Self::Base, allowing immutable functions to be called on the inner Mode.

impl<'a, Base: ?Sized> Debug for Automaton<'a, Base> where
    Base: 'a + Debug
[src]

If Base implements std::fmt::Debug, Automaton also implements Debug, and will print the current_mode.

Usage

use mode::*;
use std::fmt;
 
trait MyBase : fmt::Debug { } // TODO: Add common interface.
 
#[derive(Debug)]
struct MyMode {
    pub foo : i32,
    pub bar : &'static str,
}
 
impl MyBase for MyMode { } // TODO: Implement common interface.
 
impl<'a> Mode<'a> for MyMode {
    type Base = MyBase + 'a;
    fn as_base(&self) -> &Self::Base { self }
    fn as_base_mut(&mut self) -> &mut Self::Base { self }
    fn get_transition(&mut self) -> Option<TransitionBox<'a, Self>> { None } // TODO
}
 
let automaton = Automaton::with_initial_mode(MyMode { foo: 3, bar: "Hello, World!" });
dbg!(automaton);

impl<'a, Base: ?Sized> Display for Automaton<'a, Base> where
    Base: 'a + Display
[src]

Auto Trait Implementations

impl<'a, Base> !Send for Automaton<'a, Base>

impl<'a, Base> !Sync for Automaton<'a, Base>

Blanket Implementations

impl<T, U> Into for T where
    U: From<T>, 
[src]

impl<T> ToString for T where
    T: Display + ?Sized
[src]

impl<T> From for T[src]

impl<T, U> TryFrom for T where
    U: Into<T>, 
[src]

type Error = !

🔬 This is a nightly-only experimental API. (try_from)

The type returned in the event of a conversion error.

impl<T> Borrow for T where
    T: ?Sized
[src]

impl<T, U> TryInto for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

🔬 This is a nightly-only experimental API. (try_from)

The type returned in the event of a conversion error.

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> BorrowMut for T where
    T: ?Sized
[src]