Skip to content
This repository was archived by the owner on Jan 11, 2026. It is now read-only.
This repository was archived by the owner on Jan 11, 2026. It is now read-only.

Incorrect type is generated when using generics #27

@jquesada2016

Description

@jquesada2016

Using generics in any of the states causes the automaton's type to be stripped away. Take the following example:

use typestate::typestate;

#[typestate]
mod my_state {
    #[automaton]
    pub struct Automaton;

    #[state]
    pub struct State1<T>(T);

    pub trait State1 {
        fn new<T>() -> State1<T>
        where
            T: Default;

        fn to_state_2(self) -> State2;
    }

    impl<T> State1State for Automaton<State1<T>> {
        fn new<U>() -> State1<U>
        where
            U: Default,
        {
            State1(U::default())
        }

        fn to_state_2(self) -> Automaton<State2> {
            Automaton { state: State2 }
        }
    }

    #[state]
    pub struct State2;

    pub trait State2 {
        fn done(self);
    }

    impl State2State for Automaton<State2> {
        fn done(self) {}
    }
}

fn t() {
    use my_state::*;
    let s1: State1<()> = Automaton::<State1<()>>::new();
}

fn main() {}

cargo expand output:

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2018::*;
#[macro_use]
extern crate std;
use typestate::typestate;
///```mermaid
///stateDiagram-v2
///[*] --> State1 : new
///State1 --> State2 : to_state_2
///State2 --> [*] : done
///```
mod my_state {
    pub struct Automaton<State: AutomatonState> {
        pub state: State,
    }
    pub struct State1<T>(T);
    pub trait State1State {
        fn new<T>() -> State1<T>
        where
            T: Default;
        #[must_use]
        fn to_state_2(self) -> Automaton<State2>;
    }
    impl<T> State1State for Automaton<State1<T>> {
        fn new<U>() -> State1<U>
        where
            U: Default,
        {
            State1(U::default())
        }
        fn to_state_2(self) -> Automaton<State2> {
            Automaton { state: State2 }
        }
    }
    pub struct State2;
    pub trait State2State {
        fn done(self);
    }
    impl State2State for Automaton<State2> {
        fn done(self) {}
    }
    #[doc(hidden)]
    mod __private {
        pub trait AutomatonState {}
    }
    pub trait AutomatonState: __private::AutomatonState {}
    impl<__T: ?::core::marker::Sized> AutomatonState for __T where __T: __private::AutomatonState {}
    impl<T> __private::AutomatonState for State1<T> {}
    impl __private::AutomatonState for State2 {}
}
fn t() {
    use my_state::*;
    let s1: State1<()> = Automaton::<State1<()>>::new();
}
fn main() {}

We see that Automaton::<State1<()>>::new() yields us a type State1<()> instead of the correct type Automaton<State1<()>>. If we remove the generic parameters to State1, all is well again.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions