I am trying to generalize some logic with traits. Part of the logic is common, some is not. The common logic I am trying to address via traits, but I am not able to (lifetimes inside associated types?).
The gist is: that there is logic that depends on &B
below, but the lifetime of &B
is not easily mapped to the trait system.
Is there an idiomatic way of avoiding this?
// a struct that we receive as a reference
struct B {
b: usize
}
// under some conditions, we need to use this state
struct State1<'a> {
b: &'a B,
inner: usize,
}
// under some conditions, we need to use this state
struct State2<'a> {
b: &'a B,
inner: usize,
inner1: usize,
}
// that share common semathics
trait State<'a> {
fn add(&mut self);
}
impl<'a> State<'a> for State1<'a> {
fn add(&mut self) {
self.inner += 1;
}
}
impl<'a> State<'a> for State2<'a> {
fn add(&mut self) {
self.inner += 1;
self.inner1 += 2;
}
}
// The common logic is described in a trait
trait A {
type State: State<'a>; // <- the problem
fn build_state(b: &'a B) -> Self::State;
fn use_state(b: Self::State) -> usize;
}
// an example of an implementation of the common logic
struct A1 {}
impl<'a> A for A1 {
type State = State1<'a>;
fn build_state(b: &'a B) -> State1<'a> {
State1 {
b
}
}
fn use_state(b: Self::State) -> usize {
b.b + b.inner
}
}
// a second example of an implementation of the common logic
struct A2 {}
impl<'a> A for A2 {
type State = State2<'a>;
fn build_state(b: &'a B) -> State1<'a> {
State2 {
b
}
}
fn use_state(b: Self::State) -> usize {
b.b + b.inner + b.inner1
}
}
// the common behavior across traits
fn bla<T: A>(v: &B) -> usize {
let state = T::build_state(v);
state.add(10); // common logic from state. More complex than this...
T::use_state(state)
}
fn main() {
// we are provided with a ref, not with the owned struct
let b = &B{b: 100};
// the decision over which logic to use is known at runtime and we can branch it, like so:
let a1 = bla::<A1>(b);
let ab = bla::<A2>(b);
}