Skip to content

Conversation

@Lochlanna
Copy link

@Lochlanna Lochlanna commented Feb 9, 2026

Motivation

When working with generic state machines, you have to specify a bunch of trait bounds:

fn handle_machine<M>(machine: &mut StateMachine<M>)
where
    M: IntoStateMachine,
    M::State: State<M>,
    for<'sub> M::Superstate<'sub>: Superstate<M>,
{
    // ...
}

These bounds are verbose and add significant friction when writing generic code. I realized they're not actually needed at the call sites—they should be enforced at the trait definition level instead.

Solution

By moving the bounds for State and SuperState to the IntoStateMachine trait it makes working with a generic state machine much cleaner!

pub trait IntoStateMachine {
    type State: State<Self>;  // Bound moved here
    type Superstate<'sub>: Superstate<Self>  // Bound moved here
    where
        Self::State: 'sub;
    // ...
}

Now the same code requires only:

fn handle_machine<M: IntoStateMachine>(machine: &mut StateMachine<M>) {
    // ✨ Much cleaner!
}

Changes

  • Core traits: Bounds added to State and Superstate associated types in IntoStateMachine
  • Implementation blocks: Removed redundant bounds from ~30+ locations across:
    • blocking: inner.rs, state.rs, state_machine.rs, superstate.rs
    • awaitable: inner.rs, state.rs, state_machine.rs, superstate.rs
  • Cleanup: Removed unused imports and unnecessary lifetimes (StateExt<'a, M> → StateExt)

AI Disclosure

I did not use any AI at all for any code changes in this PR. It is completely hand written and tested by me.

I did use AI to review my changes and assist me in writing this description.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant