[−][src]Struct mode::Automaton
Represents a state machine over a set of Mode
s within the same Family
.
The Automaton
contains a single, active Mode
that represents the current state of the state machine. The current
Mode
is accessible via borrow_mode()
and borrow_mode_mut()
functions, which return an F::Base
reference, or
via Deref
coercion. The Automaton
provides a next()
function that should be called regularly in order to allow
the current state to swap in another Mode
as active, if desired.
See Mode::swap()
for more details.
Usage
use mode::*; // Use with_mode() to create the Automaton with an initial state. // NOTE: We could alternatively use SomeFamily::automaton_with_mode() here to shorten this. let mut automaton = Automaton::<SomeFamily>::with_mode(Box::new(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::next(&mut automaton);
The F
parameter
One important thing to note about the F
generic parameter it that it is not the base Mode
type that will be
stored in the Automaton
, itself. Rather, it is a separate, user-defined struct
that implements the Family
trait, representing the group of all Mode
types that are compatible with the Automaton
. For example, an
Automaton<SomeFamily>
will only be able to switch between states that implement Mode<Family = SomeFamily>
.
F::Mode
, F::Base
, and pointer types
Another important thing to understand is that the actual type stored in the Automaton
will be F::Mode
, not
F::Base
. This has to be the case because, while F::Base
can be an unsized type, e.g. a dyn Trait
, F::Mode
is
required to be a Sized
type, e.g. a Box
or an Rc
. Because of this, when a pointer type like Box
is used,
the Automaton
will actually call Mode::swap()
on the pointer wrapping the stored type. There are several
blanket impl
s for various pointer types defined in the mode
submodule that then delegate the responsibility of
switching the current Mode
to some other trait
, e.g. impl<F> Mode for Box<boxed::Mode<Family = F>>
. Please
note that boxed::Mode
is a completely different trait
than Mode
, with a swap()
method that operates on
self : Box<Self>
instead of just self
.
One advantage of having F::Mode
be a pointer type is that the inner Mode
can be a very large object that would
otherwise be slow to move into and out of the Mode::swap()
function by value. Since the convention for keeping the
Automaton
in the same state is to return self
from Mode::swap()
, moving the Mode
into and out of the
function by value would result in two needless and potentially expensive copy operations, even when switching to the
same Mode
that was current before swap()
was called. (See example below.)
use mode::{Family, Mode}; struct ReallyBigFamily; impl Family for ReallyBigFamily { type Base = ReallyBigMode; type Mode = ReallyBigMode; type Input = (); type Output = ReallyBigMode; } const DATA_SIZE : usize = 1024 * 1024; // 1 MiB struct ReallyBigMode { data : [u8; DATA_SIZE], } impl Default for ReallyBigMode { fn default() -> Self { Self { data : [0; DATA_SIZE] } } } impl Mode for ReallyBigMode { type Family = ReallyBigFamily; fn swap(self, _input : ()) -> Self { // This is silly, since we will never swap to another Mode in this scenario. However, even if we were fine // never making another Mode current like this, each call to swap() would still (potentially) move 1 MiB of // data into the function and then right back out! That's not very efficient, to say the least. self } }
Having a separate swap()
interface that operates on the pointer type itself, e.g.
fn swap(self : Box<Self>, _input : ()) -> <Self::Family as Family>::Output
in boxed::Mode
, allows the
pointer itself to be moved in and out of the swap()
function, while still delegating the responsibility of
swapping states to the stored type itself. (See example below.)
use mode::{boxed, Family}; struct ReallyBigFamily; impl Family for ReallyBigFamily { type Base = ReallyBigMode; type Mode = Box<ReallyBigMode>; type Input = (); type Output = Box<ReallyBigMode>; } const DATA_SIZE : usize = 1024 * 1024; // 1 MiB struct ReallyBigMode { data : [u8; DATA_SIZE], } impl Default for ReallyBigMode { fn default() -> Self { Self { data : [0; DATA_SIZE] } } } impl boxed::Mode for ReallyBigMode { type Family = ReallyBigFamily; fn swap(self : Box<Self>, _input : ()) -> Box<Self> { // This moves the Box back out of the function, not the object itself, which is *much* cheaper! self } }
For more on the Base
and Mode
parameters, see Family
.
Methods
impl<F: ?Sized> Automaton<F> where
F: Family,
[src]
F: Family,
pub fn with_mode(mode: F::Mode) -> Self
[src]
Creates a new Automaton
with the specified mode
, which will be the initial active Mode
for the Automaton
that is returned.
NOTE: If F::Base
is a type that implements Default
, new()
can be
used instead.
Since the F
parameter cannot be determined automatically, using this function usually requires the use of the
turbofish, e.g. Automaton::<SomeFamily>::with_mode()
. To avoid that, Family
provides an
automaton_with_mode()
associated function that can be used instead. See
Family::automaton_with_mode()
for more details.
Usage
use mode::*; struct SomeFamily; impl Family for SomeFamily { type Base = SomeMode; type Mode = SomeMode; type Input = (); type Output = SomeMode; } enum SomeMode { A, B, C }; impl Mode for SomeMode { type Family = SomeFamily; fn swap(mut self, _input : ()) -> Self { // TODO: Logic for transitioning between states goes here. self } } // Create an Automaton with A as the initial Mode. // NOTE: We could alternatively use SomeFamily::automaton_with_mode() here to shorten this. let mut automaton = Automaton::<SomeFamily>::with_mode(SomeMode::A);
impl<F: ?Sized> Automaton<F> where
F: Family,
F::Mode: Borrow<F::Base>,
[src]
F: Family,
F::Mode: Borrow<F::Base>,
pub fn borrow_mode(&self) -> &F::Base
[src]
Returns an immutable reference to the current Mode
as an &F::Base
, allowing immutable functions to be called
on the inner Mode
.
NOTE: Automaton
also implements Deref<Target = F::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
.
impl<F: ?Sized> Automaton<F> where
F: Family,
F::Mode: BorrowMut<F::Base>,
[src]
F: Family,
F::Mode: BorrowMut<F::Base>,
pub fn borrow_mode_mut(&mut self) -> &mut F::Base
[src]
Returns a mutable reference to the current Mode
as a &mut F::Base
, allowing mutable functions to be called
on the inner Mode
.
NOTE: Automaton
also implements DerefMut<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<F: ?Sized, M> Automaton<F> where
F: Family<Mode = M, Input = (), Output = M>,
M: Mode<Family = F>,
[src]
F: Family<Mode = M, Input = (), Output = M>,
M: Mode<Family = F>,
pub fn next(this: &mut Self)
[src]
Calls swap()
on the current Mode
to determine whether it wants to transition out, swapping in whatever
Mode
it returns as a result. Calling this function may change the current Mode
, but not necessarily.
See Mode::swap()
for more details.
impl<F: ?Sized, M, Input> Automaton<F> where
F: Family<Mode = M, Input = Input, Output = M>,
M: Mode<Family = F>,
[src]
F: Family<Mode = M, Input = Input, Output = M>,
M: Mode<Family = F>,
pub fn next_with_input(this: &mut Self, input: Input)
[src]
Same as Automaton::next()
, except that it passes input
into the swap()
function.
See Automaton::next()
for more details.
impl<F: ?Sized, M, Output> Automaton<F> where
F: Family<Mode = M, Input = (), Output = (M, Output)>,
M: Mode<Family = F>,
[src]
F: Family<Mode = M, Input = (), Output = (M, Output)>,
M: Mode<Family = F>,
pub fn next_with_output(this: &mut Self) -> Output
[src]
For Mode
implementations that return a tuple with a Mode
and some other parameter, calls swap()
on the
current Mode
to determine whether it wants to transition out. Whatever Mode
was returned as the first tuple
parameter will be switched in as active, and the second tuple parameter will be returned from the function.
Calling this function may change the current Mode
, but not necessarily.
See Mode::swap()
for more details.
impl<F: ?Sized, M, Input, Output> Automaton<F> where
F: Family<Mode = M, Input = Input, Output = (M, Output)>,
M: Mode<Family = F>,
[src]
F: Family<Mode = M, Input = Input, Output = (M, Output)>,
M: Mode<Family = F>,
pub fn next_with_input_and_output(this: &mut Self, input: Input) -> Output
[src]
Same as Automaton::next_with_output()
, except that it passes input
into the swap()
function.
See Automaton::next()
for more details.
impl<F: ?Sized> Automaton<F> where
F: Family,
F::Mode: Default,
[src]
F: Family,
F::Mode: Default,
pub fn new() -> Self
[src]
Creates a new Automaton
with a default Mode
instance as the active Mode
.
NOTE: This only applies if F::Base
is a concrete type that implements Default
. If F::Base
is a
trait type, or you need to specify the initial mode of the created Automaton
, use
with_mode()
instead.
Since the F
parameter cannot be determined automatically, using this function usually requires the use of the
turbofish, e.g. Automaton::<SomeFamily>::new()
. To avoid that, Family
provides an automaton()
associated
function that can be used instead. See Family::automaton()
for more
details.
Usage
use mode::*; struct ModeWithDefault { count : u32 }; impl Mode for ModeWithDefault { type Family = SomeFamily; fn swap(mut self, _input : ()) -> ModeWithDefault { // TODO: Logic for transitioning between states goes here. self.count += 1; self } } impl Default for ModeWithDefault { fn default() -> Self { ModeWithDefault { count: 0 } } } // Create an Automaton with a default Mode. // NOTE: We could alternatively use SomeFamily::automaton() here to shorten this. let mut automaton = Automaton::<SomeFamily>::new(); // NOTE: Deref coercion allows us to access the CounterMode's count variable through an Automaton reference. 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::next(&mut automaton); }
Trait Implementations
impl<F: ?Sized> AsMut<<F as Family>::Base> for Automaton<F> where
F: Family,
F::Mode: BorrowMut<F::Base>,
[src]
F: Family,
F::Mode: BorrowMut<F::Base>,
fn as_mut(&mut self) -> &mut <F as Family>::Base
[src]
Returns a mutable reference to the current Mode
as a &mut F::Base
, allowing functions to be called on the
inner Mode
.
impl<F: ?Sized> AsRef<<F as Family>::Base> for Automaton<F> where
F: Family,
F::Mode: Borrow<F::Base>,
[src]
F: Family,
F::Mode: Borrow<F::Base>,
fn as_ref(&self) -> &F::Base
[src]
Returns an immutable reference to the current Mode
as a &F::Base
, allowing functions to be called on the
inner Mode
.
impl<F: ?Sized> Debug for Automaton<F> where
F: Family,
F::Mode: Borrow<F::Base>,
F::Base: Debug,
[src]
F: Family,
F::Mode: Borrow<F::Base>,
F::Base: Debug,
If Base
implements std::fmt::Debug
, Automaton
also implements Debug
, and will print its current mode
.
Usage
use mode::*; use std::fmt::Debug; struct MyFamily; impl Family for MyFamily { type Base = dyn MyBase; type Mode = Box<dyn MyBase>; type Input = (); type Output = Box<dyn MyBase>; } trait MyBase : boxed::Mode<Family = MyFamily> + 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 boxed::Mode for MyMode { type Family = MyFamily; fn swap(self : Box<Self>, _input : ()) -> Box<dyn MyBase> { self } // TODO } let automaton = MyFamily::automaton_with_mode(Box::new(MyMode { foo: 3, bar: "Hello, World!" })); dbg!(automaton);
impl<F: ?Sized> Default for Automaton<F> where
F: Family,
F::Mode: Default,
[src]
F: Family,
F::Mode: Default,
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<F: ?Sized> Deref for Automaton<F> where
F: Family,
F::Mode: Borrow<F::Base>,
[src]
F: Family,
F::Mode: Borrow<F::Base>,
type Target = F::Base
The resulting type after dereferencing.
fn deref(&self) -> &F::Base
[src]
Returns an immutable reference to the current Mode
as a &F::Base
, allowing functions to be called on the
inner Mode
.
impl<F: ?Sized> DerefMut for Automaton<F> where
F: Family,
F::Mode: Borrow<F::Base> + BorrowMut<F::Base>,
[src]
F: Family,
F::Mode: Borrow<F::Base> + BorrowMut<F::Base>,
fn deref_mut(&mut self) -> &mut F::Base
[src]
Returns a mutable reference to the current Mode
as a &mut F::Base
, allowing functions to be called on the
inner Mode
.
impl<F: ?Sized> Display for Automaton<F> where
F: Family,
F::Mode: Borrow<F::Base>,
F::Base: Display,
[src]
F: Family,
F::Mode: Borrow<F::Base>,
F::Base: Display,
If Base
implements std::fmt::Display
, Automaton
also implements Display
, and will print its current mode
.
Usage
use mode::*; use std::fmt::{Display, Formatter, Result}; struct MyFamily; impl Family for MyFamily { type Base = dyn MyBase; type Mode = Box<dyn MyBase>; type Input = (); type Output = Box<dyn MyBase>; } trait MyBase : boxed::Mode<Family = MyFamily> + Display { } // TODO: Add common interface. struct MyMode { pub foo : i32, pub bar : &'static str, } impl Display for MyMode { fn fmt(&self, f : &mut Formatter<'_>) -> Result { write!(f, "Foo is {}, and bar is \"{}\".", self.foo, self.bar) } } impl MyBase for MyMode { } // TODO: Implement common interface. impl boxed::Mode for MyMode { type Family = MyFamily; fn swap(self : Box<Self>, _input : ()) -> Box<dyn MyBase> { self } // TODO } let automaton = MyFamily::automaton_with_mode(Box::new(MyMode { foo: 3, bar: "Hello, World!" })); println!("{}", automaton);
Auto Trait Implementations
impl<F: ?Sized> RefUnwindSafe for Automaton<F> where
<F as Family>::Mode: RefUnwindSafe,
<F as Family>::Mode: RefUnwindSafe,
impl<F: ?Sized> Send for Automaton<F> where
<F as Family>::Mode: Send,
<F as Family>::Mode: Send,
impl<F: ?Sized> Sync for Automaton<F> where
<F as Family>::Mode: Sync,
<F as Family>::Mode: Sync,
impl<F: ?Sized> Unpin for Automaton<F> where
<F as Family>::Mode: Unpin,
<F as Family>::Mode: Unpin,
impl<F: ?Sized> UnwindSafe for Automaton<F> where
<F as Family>::Mode: UnwindSafe,
<F as Family>::Mode: UnwindSafe,
Blanket Implementations
impl<T> Any for T where
T: 'static + ?Sized,
[src]
T: 'static + ?Sized,
impl<T> Borrow<T> for T where
T: ?Sized,
[src]
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
[src]
T: ?Sized,
fn borrow_mut(&mut self) -> &mut T
[src]
impl<T> From<T> for T
[src]
impl<T, U> Into<U> for T where
U: From<T>,
[src]
U: From<T>,
impl<T> ToString for T where
T: Display + ?Sized,
[src]
T: Display + ?Sized,
impl<T, U> TryFrom<U> for T where
U: Into<T>,
[src]
U: Into<T>,
type Error = Infallible
The type returned in the event of a conversion error.
fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>
[src]
impl<T, U> TryInto<U> for T where
U: TryFrom<T>,
[src]
U: TryFrom<T>,