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 模拟


用于 rust-rpg-toolkit

Apache-2.0 OR MIT

47KB
180

#mode Build Status Gitter

一个简单有效的状态机库,用惯用 Rust 编写。

mode 是什么?

此库提供三种主要类型,AutomatonModeFamily,这些类型有助于创建有限状态机。可以使用 Automaton 快速在 Family 状态集合上创建状态机,其中每个状态都是一个实现 Mode 特性的对象。

特性

  • mode 支持创建多种不同类型的状态机
    1. 简单状态机,其中每个状态都是一个单独的 enum 值,转换由外部处理。
    2. 更复杂的状态机,其中每个状态都是一个实现一些共同 dyn Trait 的单独 struct,转换到下一个 Mode 的责任委托给当前的 Mode 实现。
    3. 数据驱动状态机,其中所有状态都由具有不同输入的相同具体类型表示。
  • 可以通过包含的 Automaton 通过 Deref 强制转换轻松地将函数调用分配给当前的 Mode
  • 您完全控制对当前 Mode 的公共接口在 Automaton 之外如何暴露。
  • 灵活的转换系统允许状态机中的下一个 Mode 在转换时从上一个 Mode 中获取状态。
  • 模式 可以存储在原地或堆分配,即存储在 Box<T>Rc<T>Arc<T> 中。
  • 该库本身使用 分配。任何分配都由您控制并通过 Automaton 传入。

为什么使用 模式

  • 它非常灵活。 该库对您编写和组织代码的方式几乎没有限制。所有生命周期、分配和约定都完全由您控制。
  • 它有很好的文档。 所有公共类型都有详细的文档和示例,因此快速上手变得容易。
  • 它容易消化。 除非是示例和注释,整个库的代码行数不到 200 行。这意味着要深入了解 模式 的内部机制以了解其工作原理是轻松的。
  • 它是纯 Rust。 没有宏魔法。没有复杂的属性标记。只是 traitstruct 和泛型。
  • 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-2.0 许可证定义,应按上述方式双许可,无需任何额外条款或条件。

如果您发现错误,请随时在 GitHub 上提出问题!否则,如果您想提出对该库的更改建议,请随时向我发送拉取请求或在 模式Gitter 频道 上给我发消息。我会尽快回应这些请求。

无运行时依赖