Problem with lifetimes in associated types

Hi,

I am having a fun problem involving lifetimes, iterators and Arc, hope you can help me as I've tried every possible solution I could fine online. I am implementing a program that has support for pluggable key value stores, so I wrote a trait Database that looks something like this:

pub trait Database
where
    Self: Sized + Send + Sync,
{
    type Iterator: Iterator<Item = (Box<[u8]>, Box<[u8]>)>;

    fn iterator(
        &self,
        start: Vec<u8>,
    ) -> Result<Self::Iterator>;
}

Then the main program wraps implementations of Database in an Arc and calls Database::iterator from other threads, for example:

pub struct MyProgram<T> {
    pub db: Arc<T>
}

impl<T> MyProgram <T>
where
    T: Store + 'static,
{
    pub fn test_iterator(&self) {
	let db = self.db.clone();
        tokio::task::spawn_blocking(move || {
            for (key, value) in db.iterator("test".to_vec()).unwrap() {
                  println!("{:?} {:?}", key, value);
            }
        });
    }
}

So far everything looks good. The problem came when implementing RocksDB which requires a lifetime in the iterator:

impl Database for RocksDB {
    type Iterator = DBIteratorWithThreadMode<'a, DBWithThreadMode<MultiThreaded>>;

    fn iterator(
        &self,
        start: Vec<u8>,
    ) -> Result<Self::Iterator> {
         // code goes here
    }
}

Which of course does not compile as the lifetime 'a is not defined. So I tried multiple things:

- First I tried using PhantomData:

pub struct RocksDB<'a> {
    db: DBWithThreadMode<MultiThreaded>,
    _lifetime: PhantomData<&'a ()>,
}

impl<'a> Database for RocksDB<'a> {
    type Iterator = DBIteratorWithThreadMode<'a, DBWithThreadMode<MultiThreaded>>;
    
   fn iterator(
        &self,
        cf: store::ColumnFamily,
        start: Vec<u8>,
        direction: store::Direction,
    ) -> Result<DBIteratorWithThreadMode<'a, DBWithThreadMode<MultiThreaded>>> {
          // Implementations goes here
    }
}

Which does not work as the are conflicting lifetimes:

cannot infer an appropriate lifetime for autoref due to conflicting requirements
expected `std::result::Result<rocksdb::DBIteratorWithThreadMode<'a, _>, _>`
   found `std::result::Result<rocksdb::DBIteratorWithThreadMode<'_, _>, _>`

- Then I tried implementing the trait on references to the struct:

impl<'a> Database for &'a RocksDB {
    type Iterator = DBIteratorWithThreadMode<'a, DBWithThreadMode<MultiThreaded>>;

    // implementations goes here
}

Which looks promising but then it is not possible to allocate MyProgram as it expects implementations of Database and in this case the trait is implemented on references. Not sure if there is a way to tell the compiler that Database is implemented on &T and not T.

- I also tried adding a lifetime parameter to self on the iterator function, which is not ideal as it infects all other functions and also causes other errors.

pub trait Database<'a>
where
    Self: Sized + Send + Sync,
{
    type Iterator: Iterator<Item = (Box<[u8]>, Box<[u8]>)>;

    fn iterator(
        &'a self,
        start: Vec<u8>,
    ) -> Result<Self::Iterator>;
}

But this causes a different problem when calling db inside the spawned task:

	let db = self.db.clone();
        tokio::task::spawn_blocking(move || {
            for (key, value) in db.iterator("test".to_vec()).unwrap() {
                  println!("{:?} {:?}", key, value);
            }
        });

Indicating that the reference inside Arc is dropped while still borrowed:

`db` does not live long enough
borrowed value does not live long enough rustc E0597
bitmap.rs(334, 9): `db` dropped here while still borrowed
bitmap.rs(210, 6): lifetime `'a` defined here
bitmap.rs(283, 29): argument requires that `db` is borrowed for `'a`

Do you have any ideas of how to make this work without relying on GAT?

Thank you!

Ehh, I would try to not do this. Have you considered doing something else?

I do not what else to try unfortunately. I need to be able to abstract in a trait the iterators returned by different key value stores and can't change the trait's signature much. If you have any other alternatives to suggest please let me know, thanks!

I would expect anything putting a lifetime on the struct (as a parameter, or using &'a RocksDB) to not be able to meet your 'static bound.

Do the iterators in question have lifetimes because they are borrowing from &self? If so, that pretty much is a GAT situation. You can emulate GATs on stable if that's your concern.

Thank you, I will try that out.

Is there a reason you need an associated type? This smells like the reason for existence of impl Trait.

I just found out that you crossposted this to reddit. When you crosspost a question, you should always include a link in both places so that people don't waste their time on answering something that has already been answered.

Just because it looks cleaner, but I had tried with sub traits and had the same problems. Anyway, someone posted a fix to this issue on Reddit:

Sorry, it was my first time posting here and wasn't aware but I will keep that in mind for the next time. Also I just marked the question as solved and included a link to the Reddit thread that contains the solution.

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.