Rust有限状态机(FSM)库rust-fsm-dsl的使用,DSL驱动的高效状态机建模与实现

Rust有限状态机(FSM)库rust-fsm-dsl的使用,DSL驱动的高效状态机建模与实现

rust-fsm crate提供了一个简单通用的框架,用于在Rust中构建状态机。它的核心是StateMachineImpl trait,允许开发者严格定义状态机的以下方面:

  • 输入字母表 - 状态机接收的输入集合
  • 可能的状态 - 状态机可能处于的状态集合
  • 输出字母表 - 状态机工作的输出结果集合
  • 转移函数 - 基于当前状态和输入改变状态的函数
  • 输出函数 - 基于当前状态和输入产生输出的函数
  • 初始状态

使用DSL定义状态机

DSL通过state_machine宏解析。以下是一个断路器状态机的示例:

use rust_fsm::*;

state_machine! {
    #[derive(Debug)]
    #[repr(C)]
    /// 一个断路器状态机
    circuit_breaker(Closed)

    Closed(Unsuccessful) => Open [SetupTimer],
    Open(TimerTriggered) => HalfOpen,
    HalfOpen => {
        Successful => Closed,
        Unsuccessful => Open [SetupTimer]
    }
}

这个代码定义了一个名为circuit_breaker的状态机:

  1. 初始状态为Closed
  2. 定义了状态转移:例如在HalfOpen状态收到Successful输入时转移到Closed状态
  3. 定义了输出:例如在Closed状态收到Unsuccessful输入时输出SetupTimer

使用示例:

// 初始化状态机,状态为Closed
let mut machine = circuit_breaker::StateMachine::new();
// 消费Successful输入,不进行状态转移
let _ = machine.consume(&circuit_breaker::Input::Successful);
// 消费Unsuccessful输入,状态转移到Open,输出SetupTimer
let output = machine.consume(&circuit_breaker::Input::Unsuccessful).unwrap();
// 检查输出
if let Some(circuit_breaker::Output::SetupTimer) = output {
    // 设置定时器...
}
// 检查状态
if let circuit_breaker::State::Open = machine.state() {
    // 做一些事情...
}

完整示例

以下是使用DSL定义状态机的完整示例代码:

use rust_fsm::*;

// 定义状态机
state_machine! {
    #[derive(Debug)]
    /// 门禁系统状态机
    door_access(Closed)

    Closed(KeyInserted) => Opened,
    Opened => {
        KeyRemoved => Closed,
        Timeout => Closed [SoundAlarm]
    }
}

fn main() {
    // 创建状态机实例
    let mut door = door_access::StateMachine::new();
    
    // 初始状态应为Closed
    assert!(matches!(door.state(), door_access::State::Closed));
    
    // 插入钥匙开门
    let output = door.consume(&door_access::Input::KeyInserted);
    assert!(output.unwrap().is_none()); // 无输出
    assert!(matches!(door.state(), door_access::State::Opened));
    
    // 钥匙移除关门
    let output = door.consume(&door_access::Input::KeyRemoved);
    assert!(output.unwrap().is_none());
    assert!(matches!(door.state(), door_access::State::Closed));
    
    // 再次开门
    door.consume(&door_access::Input::KeyInserted).unwrap();
    
    // 超时关门并触发警报
    let output = door.consume(&door_access::Input::Timeout).unwrap();
    assert!(matches!(output, Some(door_access::Output::SoundAlarm)));
    assert!(matches!(door.state(), door_access::State::Closed));
    
    println!("Door access state machine test completed successfully!");
}

使用自定义类型

您可以使用自己的类型作为输入、输出或状态:

use rust_fsm::*;

pub enum Input {
    KeyInserted,
    KeyRemoved,
    Timeout,
}

pub enum State {
    Closed,
    Opened,
}

pub enum Output {
    SoundAlarm,
}

state_machine! {
    #[state_machine(input(crate::Input), state(crate::State), output(crate::Output))]
    door_access(Closed)

    Closed(KeyInserted) => Opened,
    Opened => {
        KeyRemoved => Closed,
        Timeout => Closed [SoundAlarm]
    }
}

特性标志

默认特性

  • std - 需要std环境的特性
  • dsl - 从rust-fsm重新导出rust-fsm-dsl,推荐开启

非默认特性

  • diagram - 在文档注释中生成Mermaid状态图

no_std环境中使用

默认启用了std特性。要在no_std环境中使用,可以这样导入:

rust-fsm = { version = "0.8", default-features = false, features = ["dsl"] }

安装

在项目目录中运行以下Cargo命令:

cargo add rust-fsm-dsl

或者在Cargo.toml中添加:

rust-fsm-dsl = "0.8.0"

完整Demo示例

下面是一个完整的交通灯状态机示例:

use rust_fsm::*;

// 定义交通灯状态机
state_machine! {
    #[derive(Debug)]
    /// 交通灯状态机
    traffic_light(Red)

    Red(Timer) => Green,
    Green(Timer) => Yellow,
    Yellow(Timer) => Red
}

fn main() {
    // 创建状态机实例
    let mut light = traffic_light::StateMachine::new();
    
    // 初始状态应为Red
    assert!(matches!(light.state(), traffic_light::State::Red));
    
    // 打印当前状态
    println!("Current state: {:?}", light.state());
    
    // 红灯变绿灯
    light.consume(&traffic_light::Input::Timer).unwrap();
    println!("State changed to: {:?}", light.state());
    assert!(matches!(light.state(), traffic_light::State::Green));
    
    // 绿灯变黄灯
    light.consume(&traffic_light::Input::Timer).unwrap();
    println!("State changed to: {:?}", light.state());
    assert!(matches!(light.state(), traffic_light::State::Yellow));
    
    // 黄灯变红灯
    light.consume(&traffic_light::Input::Timer).unwrap();
    println!("State changed to: {:?}", light.state());
    assert!(matches!(light.state(), traffic_light::State::Red));
    
    println!("Traffic light state machine test completed successfully!");
}

这个示例展示了:

  1. 定义了一个简单的交通灯状态机,包含Red、Green和Yellow三个状态
  2. 每个状态在收到Timer输入时都会转移到下一个状态
  3. 通过断言验证状态转移的正确性
  4. 打印状态变化过程

1 回复

Rust有限状态机(FSM)库rust-fsm-dsl使用指南

简介

rust-fsm-dsl是一个基于领域特定语言(DSL)的Rust有限状态机(FSM)库,它提供了一种声明式的方式来定义和实现状态机。这个库特别适合需要清晰状态转换逻辑的应用场景,如协议实现、游戏AI、业务工作流等。

主要特性

  • 简洁的DSL语法定义状态机
  • 编译时状态转换验证
  • 零成本抽象
  • 支持状态进入/退出动作
  • 类型安全的状态管理

基本使用方法

1. 添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
rust-fsm-dsl = "0.3"

2. 定义状态机

使用DSL宏定义状态机:

use rust_fsm_dsl::*;

state_machine! {
    derive(Debug)
    CircuitBreaker(Closed)

    Closed => Open [Failure]
    Open => HalfOpen [Timer]
    HalfOpen => Open [Failure]
    HalfOpen => Closed [Success]
}

3. 使用状态机

fn main() {
    // 创建状态机实例,初始状态为Closed
    let mut machine: StateMachine<CircuitBreaker> = StateMachine::new();
    
    // 处理事件
    let result = machine.consume(&CircuitBreakerInput::Failure);
    assert!(matches!(result, Ok(Some(CircuitBreakerOutput::Open))));
    
    // 检查当前状态
    assert!(matches!(machine.state(), CircuitBreakerState::Open));
}

完整示例代码

use rust_fsm_dsl::*;

// 定义订单状态机
state_machine! {
    derive(Debug, Clone)
    OrderStateMachine(New)

    New => PendingPayment [Submit]
    PendingPayment => Paid [Pay(amount: f64)]
    PendingPayment => Cancelled [Cancel(reason: String)]
    Paid => Shipped [Ship(tracking_number: String)]
    Shipped => Delivered [Deliver]
    Shipped => Returned [Return(reason: String)]
    
    impl OrderStateMachine {
        fn on_pay(&mut self, amount: f64) -> Result<(), String> {
            if amount <= 0.0 {
                Err("Payment amount must be positive".to_string())
            } else {
                self.amount_paid = amount;
                Ok(())
            }
        }
        
        fn on_ship(&mut self, tracking_number: String) {
            self.tracking_number = Some(tracking_number);
        }
    }
}

fn main() {
    // 创建订单状态机实例
    let mut order_machine: StateMachine<OrderStateMachine> = StateMachine::new();
    
    // 提交订单
    let result = order_machine.consume(&OrderStateMachineInput::Submit);
    assert!(matches!(result, Ok(Some(OrderStateMachineOutput::PendingPayment))));
    
    // 支付订单
    let pay_result = order_machine.consume(&OrderStateMachineInput::Pay(100.0));
    assert!(matches!(pay_result, Ok(Some(OrderStateMachineOutput::Paid))));
    
    // 发货
    let ship_result = order_machine.consume(&OrderStateMachineInput::Ship("123456789".to_string()));
    assert!(matches!(ship_result, Ok(Some(OrderStateMachineOutput::Shipped))));
    
    // 检查当前状态
    assert!(matches!(order_machine.state(), OrderStateMachineState::Shipped));
    
    println!("Order state: {:?}", order_machine.state());
}

最佳实践

  1. 为每个状态机定义单独的模块
  2. 使用#[derive(Debug)]以便调试
  3. 对关键业务逻辑添加单元测试
  4. 考虑使用typenum crate处理复杂状态条件
  5. 对于性能敏感场景,考虑使用no_std模式

性能考虑

rust-fsm-dsl在编译时生成状态转换表,运行时开销极小。状态转换是O(1)操作,只涉及简单的表查找和函数调用。

总结

rust-fsm-dsl提供了一种类型安全且高效的方式来在Rust中实现有限状态机。它的DSL语法使得状态机定义清晰易懂,同时保持了Rust的性能优势。通过编译时验证,可以避免许多常见的状态机实现错误。

回到顶部