Putting lifetimes shorter than 'static in trait implementation

Hi, I’m trying to implement a trait in my code:

pub trait AtomicDb {
    type State;
    type Mutation;
    type Error;

    fn get_state(&self) -> &Self::State;
    fn mutate(&mut self, mutations: Vec<Self::Mutation>) -> Result<(), Self::Error>;
}

As follows:

struct MockDb<A: Clone> {
    state: FunderState<A>,
}

impl<A: Clone + 'static> AtomicDb for MockDb<A> {
    type State = FunderState<A>;
    type Mutation = FunderMutation<A>;
    type Error = ();

    fn get_state(&self) -> &FunderState<A> {
        &self.state
    }

    fn mutate(&mut self, mutations: Vec<FunderMutation<A>>) -> Result<(), ()> {
        for mutation in mutations {
            self.state.mutate(&mutation);
        }
        Ok(())
    }
}

This code currently compiles, but I think that the A: 'static requirement could be relaxed. I tried to change it to something else, like this:


impl<'a, A: Clone + 'a> AtomicDb for MockDb<A> {
    type State = FunderState<A>;
    type Mutation = FunderMutation<A>;
    type Error = ();

    fn get_state(&'a self) -> &'a FunderState<A> {
        &self.state
    }

    fn mutate(&'a mut self, mutations: Vec<FunderMutation<A>>) -> Result<(), ()> {
        for mutation in mutations {
            self.state.mutate(&mutation);
        }
        Ok(())
    }
}

And I got the following error messages:

error[E0308]: method not compatible with trait
   --> components/funder/src/tests.rs:113:5
    |
113 |     fn get_state(&'a self) -> &'a FunderState<A> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
    |
    = note: expected type `fn(&tests::MockDb<A>) -> &state::FunderState<A>`
               found type `fn(&'a tests::MockDb<A>) -> &'a state::FunderState<A>`
note: the lifetime 'a as defined on the impl at 108:6...
   --> components/funder/src/tests.rs:108:6
    |
108 | impl<'a, A: Clone + 'a> AtomicDb for MockDb<A> {
    |      ^^
note: ...does not necessarily outlive the anonymous lifetime #1 defined on the method body at 113:5
   --> components/funder/src/tests.rs:113:5
    |
113 | /     fn get_state(&'a self) -> &'a FunderState<A> {
114 | |         &self.state
115 | |     }
    | |_____^

error[E0308]: method not compatible with trait
   --> components/funder/src/tests.rs:117:5
    |
117 |     fn mutate(&'a mut self, mutations: Vec<FunderMutation<A>>) -> Result<(), ()> {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime mismatch
    |
    = note: expected type `fn(&mut tests::MockDb<A>, std::vec::Vec<state::FunderMutation<A>>) -> std::result::Result<(), ()>`
               found type `fn(&'a mut tests::MockDb<A>, std::vec::Vec<state::FunderMutation<A>>) -> std::result::Result<(), ()>`
note: the anonymous lifetime #1 defined on the method body at 117:5...
   --> components/funder/src/tests.rs:117:5
    |
117 | /     fn mutate(&'a mut self, mutations: Vec<FunderMutation<A>>) -> Result<(), ()> {
118 | |         for mutation in mutations {
119 | |             self.state.mutate(&mutation);
120 | |         }
121 | |         Ok(())
122 | |     }
    | |_____^
note: ...does not necessarily outlive the lifetime 'a as defined on the impl at 108:6
   --> components/funder/src/tests.rs:108:6
    |
108 | impl<'a, A: Clone + 'a> AtomicDb for MockDb<A> {

What would be the correct way to relax the 'static requirement?

Simply removing the lifetime bound seems to work:

impl<A: Clone> AtomicDb for MockDb<A> { ... }

Playground

@Riateche: Thanks for the quick reply. Strangely, when I try to remove the 'static hint I get this compilation error:

error[E0310]: the parameter type `A` may not live long enough
   --> components/funder/src/tests.rs:131:13
    |
131 |             self.state.mutate(&mutation);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider adding an explicit lifetime bound `A: 'static`...

Maybe this problem is related to how FunderState or FunderMutation are defined?
If this is of any help, my definitions are as follows:

#[derive(Clone, Serialize, Deserialize)]
pub struct FunderState<A:Clone> {
    pub local_public_key: PublicKey,
    pub friends: ImHashMap<PublicKey, FriendState<A>>,
    pub ready_receipts: ImHashMap<Uid, SendFundsReceipt>,
}

#[derive(Debug)]
pub enum FunderMutation<A> {
    FriendMutation((PublicKey, FriendMutation<A>)),
    AddFriend(AddFriend<A>), 
    RemoveFriend(PublicKey),
    AddReceipt((Uid, SendFundsReceipt)),  //(request_id, receipt)
    RemoveReceipt(Uid),
}

As can be seen here:

Yes, examine this part of the code:

impl<A: Clone + 'static> FunderState<A> {
    ...
    pub fn mutate(&mut self, messenger_mutation: &FunderMutation<A>) { ... }
}

This means that mutate() requires 'static bound on A.

You should check if you can relax the requirements there. You can place methods in separate impl blocks to refine the requirements needed for each method.

1 Like

Thank you for your help!
I didn’t notice that I had that 'static in the implementation.