Trait with associated type with lifetime

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);
}

Try this:

1 Like

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.