Is typestate as traits possible?

Currently there is the typestate pattern, where you use types to control state and thus possible behaviour:

// sets One and Two as states of State
pub trait State {}
struct One {}
struct Two {}
impl State for One {}
impl State for Two {}

struct Foo<S: State> {
    state: S
}

// only callable if Foo's state is One
impl Foo<One> {
    fn two(self) -> Foo<Two> {
        Foo { state: Two {} }
    }
} 

// only callable if Foo's state is Two
impl Foo<Two> {
    fn one(self) -> Foo<One> {
        Foo { state: One {} }
    }
}

Is it possible to have something like typestate as traits? Ie:

// doesn't compile, just a rough idea
trait FooTrait<One> {
    fn next_two(self) -> Self<Two>;
} 

trait FooTrait<Two> {
    fn next_one(self) -> Self<One>;
} 

/*
Bar now has its own behaviour for these,
but adheres to the same typestate system.
*/
struct Bar<S: State> {
    state: S
}

impl FooTrait<One> for Bar {
    fn next(self) -> Self<Two> {
        println!("different!");
        Bar { state: Two {} }
    }
}

impl FooTrait<Two> for Bar {
    fn next(self) -> Self<One> {
        println!("different!");
        Bar { state: One {} }
    }
}

Yes; google "generic associated type".

Thanks, so if I'm not mistaken it will be something like this?

// to transition to State = Two
trait FooOne {
    type Obj<S: State>;

    fn two(self) -> Self::Obj<Two>;
} 

// to transition to State = One
trait FooTwo {
    type Obj<S: State>;

    fn one(self) -> Self::Obj<One>;
} 

// generic struct
struct Bar<S: State> {
    state: S
}

// Bar<One> can transition to Two
impl FooOne for Bar<One> {
    type Obj<S: State> = Bar<Two>;

    fn two(self) -> Self::Obj<Two> {
        println!("different!");
        Bar { state: Two {} }
    }
}

// and Bar<Two> can transition to One
impl FooTwo for Bar<Two> {
    type Obj<One: State> = Bar<One>;

    fn one(self) -> Self::Obj<One> {
        println!("different!");
        Bar { state: One {} }
    } 
}

However, is there any way within the traits to ensure self is generic over S: State, but have concrete S? Currently anything can implement FooOne and FooTwo, but the intention is that they should also be Self::Obj<Two> and Self::Obj<One>.

You don't need the generic type parameter on the associated type. And just a single trait can be used to transition to the next state as in OP: Rust Playground

This cannot lead to any ambiguity, because there is only one valid transition from Bar<One>. So, it doesn't matter what other types implement the trait.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.