10 个版本
0.4.1 | 2021 年 8 月 13 日 |
---|---|
0.4.0 | 2020 年 3 月 21 日 |
0.3.0 | 2019 年 12 月 20 日 |
0.2.4 | 2019 年 10 月 3 日 |
0.1.1 | 2019 年 2 月 8 日 |
#390 in 模拟
47KB
180 行
一个简单有效的状态机库,用惯用 Rust 编写。
mode
是什么?
此库提供三种主要类型,Automaton
、Mode
和 Family
,这些类型有助于创建有限状态机。可以使用 Automaton
快速在 Family
状态集合上创建状态机,其中每个状态都是一个实现 Mode
特性的对象。
特性
mode
支持创建多种不同类型的状态机- 简单状态机,其中每个状态都是一个单独的
enum
值,转换由外部处理。 - 更复杂的状态机,其中每个状态都是一个实现一些共同
dyn Trait
的单独struct
,转换到下一个Mode
的责任委托给当前的Mode
实现。 - 数据驱动状态机,其中所有状态都由具有不同输入的相同具体类型表示。
- 简单状态机,其中每个状态都是一个单独的
- 可以通过包含的
Automaton
通过Deref
强制转换轻松地将函数调用分配给当前的Mode
。 - 您完全控制对当前
Mode
的公共接口在Automaton
之外如何暴露。 - 灵活的转换系统允许状态机中的下一个
Mode
在转换时从上一个Mode
中获取状态。 模式
可以存储在原地或堆分配,即存储在Box<T>
、Rc<T>
或Arc<T>
中。- 该库本身使用 零 分配。任何分配都由您控制并通过
Automaton
传入。
为什么使用 模式
?
- 它非常灵活。 该库对您编写和组织代码的方式几乎没有限制。所有生命周期、分配和约定都完全由您控制。
- 它有很好的文档。 所有公共类型都有详细的文档和示例,因此快速上手变得容易。
- 它容易消化。 除非是示例和注释,整个库的代码行数不到 200 行。这意味着要深入了解
模式
的内部机制以了解其工作原理是轻松的。 - 它是纯 Rust。 没有宏魔法。没有复杂的属性标记。只是
trait
、struct
和泛型。 - 100% 安全,100% 稳定。 该库中没有
unsafe
块,也没有需要nightly
工具链的功能。这意味着模式
是可靠且健壮的。
版本
请参阅 GitHub 上的完整版本列表:GitHub。
升级到版本 0.4
在版本 0.4
中,我们进行了大量的优化,以使 模式
更容易理解和使用。如果您想将项目从版本 0.3
升级到 0.4
的 模式
,请参阅 UPGRADING-v0.4.md 以获取逐步指南。
文档
请参阅 docs.rs 以获取详细文档。
示例
use mode::{Automaton, Family, Mode};
// This meta-struct represents a group of all Modes that can be used with the same Automaton, i.e. all states in the
// same state machine. By implementing Family, we can specify the common interface that will be exposed for all states
// (type Base) and how the current state will be stored in the Automaton (type Mode). The important thing to note is
// that this struct will never be instantiated. It only exists to group a set of states (Modes) together.
//
struct ActivityFamily;
impl Family for ActivityFamily {
// This is the public interface that will be exposed by the Automaton for all Modes in this Family.
type Base = dyn Activity;
// This is the type that will be stored in the Automaton and passed into the Automaton::next() function.
type Mode = Box<dyn Activity>;
}
// This trait defines a common interface for all Modes in ActivityFamily.
//
trait Activity : Mode<Family = ActivityFamily> {
fn update(self : Box<Self>) -> Box<dyn Activity>;
}
// Each state in the state machine implements both Activity (the Base type) and Mode.
//
struct Working {
pub hours_worked : u32,
}
impl Mode for Working {
type Family = ActivityFamily;
}
impl Activity for Working {
// This function updates the Mode and allows it to swap another one in as current, when ready.
//
fn update(mut self : Box<Self>) -> Box<dyn Activity> {
println!("Work, work, work...");
self.hours_worked += 1;
if self.hours_worked == 4 || self.hours_worked >= 8 {
// To swap to another Mode, we can return a new, boxed Mode with the same signature as this one. Note that
// because this function consumes the input Box<Self>, we can freely move state out of this Mode and into
// the new one that will be swapped in.
println!("Time for {}!", if self.hours_worked == 4 { "lunch" } else { "dinner" });
Box::new(Eating { hours_worked: self.hours_worked, calories_consumed: 0 })
}
else { self } // Returning self means that this Mode should remain current.
}
}
struct Eating {
pub hours_worked : u32,
pub calories_consumed : u32,
}
impl Mode for Eating {
type Family = ActivityFamily;
}
impl Activity for Eating {
fn update(mut self : Box<Self>) -> Box<dyn Activity> {
println!("Yum!");
self.calories_consumed += 100;
if self.calories_consumed >= 500 {
if self.hours_worked >= 8 {
println!("Time for bed!");
Box::new(Sleeping { hours_rested: 0 })
}
else {
println!("Time to go back to work!");
Box::new(Working { hours_worked: self.hours_worked })
}
}
else { self }
}
}
struct Sleeping {
pub hours_rested : u32,
}
impl Mode for Sleeping {
type Family = ActivityFamily;
}
impl Activity for Sleeping {
fn update(mut self : Box<Self>) -> Box<dyn Activity> {
println!("ZzZzZzZz...");
self.hours_rested += 1;
if self.hours_rested >= 8 {
println!("Time for breakfast!");
Box::new(Eating { hours_worked: 0, calories_consumed: 0 })
}
else { self }
}
}
fn main() {
let mut person = ActivityFamily::automaton_with_mode(Box::new(Working { hours_worked: 0 }));
for _age in 18..100 {
// Update the current Mode and/or transition to another Mode, when the current Mode requests it.
Automaton::next(&mut person, |current_mode| current_mode.update());
}
}
许可
许可方式如下:
- Apache License,版本 2.0(《LICENSE-APACHE》或https://apache.ac.cn/licenses/LICENSE-2.0)
- MIT 许可(《LICENSE-MIT》或http://opensource.org/licenses/MIT)
任选其一。
贡献
除非您明确表示,否则任何有意提交给作品并由您包括在内的贡献,根据 Apache-2.0 许可证定义,应按上述方式双许可,无需任何额外条款或条件。
如果您发现错误,请随时在 GitHub 上提出问题!否则,如果您想提出对该库的更改建议,请随时向我发送拉取请求或在 模式
的 Gitter 频道 上给我发消息。我会尽快回应这些请求。