You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust带闭包的泛型有限状态机实现问题求助

解决Rust泛型闭包FSM的两个问题

问题1:移除多余的PhantomData

你需要调整结构体的泛型参数定义,让输入、输出类型直接通过闭包的签名关联,而非单独声明未使用的泛型参数。

原问题原因

当泛型参数(比如你的A、B)在结构体中没有被直接使用时,Rust编译器会判定它们为多余参数,因此要求添加PhantomData来“占位”。但实际上这些类型完全可以通过闭包的函数签名推导出来。

修正后的结构体定义

// 直接将输入(I)、状态(S)、输出(O)作为泛型参数,闭包类型关联这些参数
struct FSM<I, S, O, U, Oup>
where
    U: Fn(I, S) -> S,
    Oup: Fn(I, S) -> O,
{
    update: U,
    output: Oup,
    state: S,
}

impl<I, S, O, U, Oup> FSM<I, S, O, U, Oup> {
    // 构造函数:直接推导泛型参数,无需手动指定PhantomData
    fn new(initial_state: S, update: U, output: Oup) -> Self {
        Self {
            state: initial_state,
            update,
            output,
        }
    }

    // 运行FSM的示例方法
    fn step(&mut self, input: I) -> O
    where
        S: Clone,
    {
        let current_state = self.state.clone();
        let output = (self.output)(input.clone(), current_state);
        self.state = (self.update)(input, self.state);
        output
    }
}

这样调整后,I(输入类型)、O(输出类型)都通过闭包U、Oup的签名被直接使用,编译器不再需要PhantomData来满足泛型参数的使用要求。

问题2:修复product方法的闭包类型不匹配错误

product方法的核心问题是:闭包是匿名类型,无法提前通过泛型参数U3、O3声明。解决方法是使用impl Trait来指定返回值中闭包的 trait 约束,而非尝试命名匿名的闭包类型。

修正后的product方法实现

impl<I, S1, O1, U1, Oup1> FSM<I, S1, O1, U1, Oup1>
where
    U1: Clone,
    Oup1: Clone,
    I: Clone,
{
    // 组合两个FSM为乘积FSM:状态为元组,输出为元组,输入共享同一类型
    fn product<S2, O2, U2, Oup2>(self, other: FSM<I, S2, O2, U2, Oup2>) -> FSM<I, (S1, S2), (O1, O2), impl Fn(I, (S1, S2)) -> (S1, S2), impl Fn(I, (S1, S2)) -> (O1, O2)>
    where
        U2: Clone,
        Oup2: Clone,
    {
        let update_self = self.update;
        let update_other = other.update;
        let output_self = self.output;
        let output_other = other.output;

        FSM::new(
            (self.state, other.state),
            // 组合update闭包:分别更新两个子状态
            move |input, (s1, s2)| {
                let new_s1 = update_self(input.clone(), s1);
                let new_s2 = update_other(input, s2);
                (new_s1, new_s2)
            },
            // 组合output闭包:分别生成两个子输出
            move |input, (s1, s2)| {
                let out1 = output_self(input.clone(), s1);
                let out2 = output_other(input, s2);
                (out1, out2)
            },
        )
    }
}

关键说明

  1. 使用impl Fn(...) -> ...作为返回值中闭包的类型,告诉编译器:返回的闭包实现了对应的Fn trait,无需显式命名匿名类型。
  2. 添加Clone约束:因为闭包需要捕获并复用原FSM的update和output闭包,所以需要这些闭包支持克隆(如果你的闭包无法克隆,可以改用Arc来共享)。
  3. 输入类型I添加Clone约束:因为需要将输入传递给两个子FSM的闭包。

测试示例

fn main() {
    // 第一个FSM:计数器,输入为bool,true则+1,输出当前值
    let mut counter_fsm = FSM::new(
        0,
        |inc: bool, state: i32| if inc { state + 1 } else { state },
        |_inc: bool, state: i32| state,
    );

    // 第二个FSM:布尔翻转器,输入为bool,true则翻转状态,输出当前状态
    let mut toggle_fsm = FSM::new(
        false,
        |toggle: bool, state: bool| if toggle { !state } else { state },
        |_toggle: bool, state: bool| state,
    );

    // 组合为乘积FSM
    let mut product_fsm = counter_fsm.product(toggle_fsm);

    // 测试步骤
    assert_eq!(product_fsm.step(true), (1, true));
    assert_eq!(product_fsm.step(false), (1, true));
    assert_eq!(product_fsm.step(true), (2, false));
}

内容的提问来源于stack exchange,提问作者lighthouse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 01:55:34