7 个版本 (4 个重大更改)
| 0.7.0 | 2024 年 6 月 1 日 | 
|---|---|
| 0.6.2 | 2024 年 5 月 11 日 | 
| 0.6.1 | 2022 年 12 月 24 日 | 
| 0.6.0 | 2021 年 8 月 24 日 | 
| 0.3.0 | 2019 年 5 月 22 日 | 
#2353 在 Rust 模式
12,725 每月下载量
用于 rust-fsm
22KB
321 行
一个用于在 Rust 中构建有限状态机的框架和 DSL
rust-fsm 包提供了一个简单且通用的框架,用于以最小的努力在 Rust 中构建状态机。
此包的核心部分是 StateMachineImpl 特性。此特性允许开发者提供严格的状态机定义,例如指定其
- 输入字母表 - 状态机作为输入接受的一组实体,并根据它们执行状态转换。
- 可能的状态 - 此机器可能处于的一组状态。
- 输出字母表 - 状态机可能作为其工作结果输出的实体集合。
- 转换函数 - 根据当前状态和提供的输入更改状态机状态的函数。
- 输出函数 - 根据当前状态和提供的输入从输出字母表中输出内容的函数。
- 机器的初始状态。
请注意,在实现层面上,此类抽象允许构建任何类型的状态机
- 通过仅提供输入字母表、状态集和转换函数来构建经典状态机。
- 通过提供所有上述实体来构建 Mealy 机器。
- 通过提供不依赖于提供的输入的输出函数来构建 Moore 机器。
功能标志
默认
- std- 实现- std环境所需的特性。见下文。
- dsl- 从- rust-fsm重新导出- rust-fsm-dsl。建议保留此特性以获得最佳开发体验。
非默认
- diagram- 在文档字符串中生成Mermaid状态图。见下文。
在no_std环境中的使用
该库具有名为std的功能,默认启用。您可能希望将此库导入为rust-fsm = { version = "0.7", default-features = false, features = ["dsl"] },以在no_std环境中使用它。这仅影响错误类型(Error特质仅在std中可用)。
DSL实现的重导出受名为dsl的功能控制,该功能默认启用。
使用方法
最初,此库的设计是为了构建一个易于使用的DSL,用于在其之上定义状态机。使用DSL需要连接一个额外的crate rust-fsm-dsl(这是由于过程宏系统限制所致)。
使用DSL定义状态机
DSL由state_machine宏解析。以下是一个小示例。
use rust_fsm::*;
state_machine! {
    #[derive(Debug)]
    #[repr(C)]
    /// A Circuit Breaker state machine.
    circuit_breaker(Closed)
    Closed(Unsuccessful) => Open [SetupTimer],
    Open(TimerTriggered) => HalfOpen,
    HalfOpen => {
        Successful => Closed,
        Unsuccessful => Open [SetupTimer]
    }
}
此代码示例
- 定义了一个名为circuit_breaker的状态机;
- 为它推导了Debug特质。您在这里使用的所有属性(如#[repr(C)])将应用于此宏生成的所有类型。如果您想将属性或文档字符串应用于此宏生成的mod,只需将其放在宏调用之前即可。
- 将此状态机的初始状态设置为Closed;
- 定义状态转换。例如:在HalfOpen状态下收到Successful输入时,机器必须移动到Closed状态;
- 定义输出。例如:在Closed状态下收到Unsuccessful时,机器必须输出SetupTimer。
可以使用以下方式使用此状态机
// Initialize the state machine. The state is `Closed` now.
let mut machine = circuit_breaker::StateMachine::new();
// Consume the `Successful` input. No state transition is performed.
let _ = machine.consume(&circuit_breaker::Input::Successful);
// Consume the `Unsuccesful` input. The machine is moved to the `Open`
// state. The output is `SetupTimer`.
let output = machine.consume(&circuit_breaker::Input::Unsuccessful).unwrap();
// Check the output
if let Some(circuit_breaker::Output::SetupTimer) = output {
    // Set up the timer...
}
// Check the state
if let circuit_breaker::State::Open = machine.state() {
    // Do something...
}
以下实体被生成
- 一个空结构circuit_breaker::Impl,它实现了StateMachineImpl特质。
- 枚举circuit_breaker::State、circuit_breaker::Input和circuit_breaker::Output,分别表示状态、输入字母表和输出字母表。
- 类型别名circuit_breaker::StateMachine,它扩展为StateMachine<circuit_breaker::Impl>。
注意,如果规范中没有输出,输出字母表是一个空枚举,并且由于许多Rust属性的技術限制,没有应用于它(例如,derive、repr)。
在 state_machine 宏中,您必须定义至少一个状态转换。
可见性
您可以使用以下方式指定可见性
use rust_fsm::*;
state_machine! {
    pub CircuitBreaker(Closed)
    Closed(Unsuccessful) => Open [SetupTimer],
    Open(TimerTriggered) => HalfOpen,
    HalfOpen => {
        Successful => Closed,
        Unsuccessful => Open [SetupTimer],
    }
}
默认可见性是私有的。
自定义字母类型
您可以将自己的类型提供作为输入、输出或状态的用途。所有这些都是可选的:您可以使用它们中的一个或全部,根据需要。当前的限制是您必须提供完全限定的类型路径。
use rust_fsm::*;
pub enum Input {
    Successful,
    Unsuccessful,
    TimerTriggered,
}
pub enum State {
    Closed,
    HalfOpen,
    Open,
}
pub enum Output {
    SetupTimer,
}
state_machine! {
    #[state_machine(input(crate::Input), state(crate::State), output(crate::Output))]
    circuit_breaker(Closed)
    Closed(Unsuccessful) => Open [SetupTimer],
    Open(TimerTriggered) => HalfOpen,
    HalfOpen => {
        Successful => Closed,
        Unsuccessful => Open [SetupTimer]
    }
}
图表
state_machine 宏可以使用图表来记录状态机。这由非默认的 diagram 功能控制。图表以 Mermaid 格式生成。此功能将 Mermaid 脚本包含到文档页面中。
要查看实际效果,请下载仓库并运行
cargo doc -p doc-example --open

没有 DSL
state_machine 宏的功能有限(例如,状态不能携带任何附加数据),因此在某些复杂情况下,用户可能想手动编写更复杂的状态机。
构建状态机所需的所有操作是实现 StateMachineImpl 特性,并使用一些提供的包装器(目前只有 StateMachine)。
您可以在 项目仓库 中看到电路断开状态机的示例。
依赖项
~240–680KB
~16K SLoC