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 {} }
}
}
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.