I've got a small RPC network protocol I've written, and there's a server-streaming method (very similar to gRPC style server-streaming) called range
. The client sends start and end keys, and the server streams the values back. I'm using the tower crate as an interface in between the network protocol library and the application code that actually implements an instance of this service. I'd like to use the nice rocksdb wrapper that exists for Rust, but I'm running into lifetime issues. In the short term since I couldn't figure them out, I decided to use sled. That worked great. However, I'd prefer to use RocksDB if I can since sled still markets itself as a beta database.
Here's a distilled version of the code involved in the problem:
use std::ops::Range;
use futures_util::{Stream, Future, future::{Ready, ready}, stream};
use rocksdb::{ReadOptions, IteratorMode, Error, DB, DBIterator};
// Service is more complicated than this, but here are the important bits
pub trait Service {
type RequestFuture: Future<Output = Self::RequestStream>;
type RequestStream: Stream<Item = Self::RequestItem>;
type RequestItem;
fn range(&self, range: Range<Vec<u8>>) -> Self::RequestFuture;
}
pub async fn network_protocol_code<T: Service>(service: T)
where
T::RequestStream: Send + 'static,
T::RequestItem: Send + 'static
{
// use service to send responses to the client
}
pub struct ExampleService {
database: DB
}
impl Service for ExampleService {
type RequestFuture = Ready<Self::RequestStream>;
type RequestStream = stream::Iter<DBIterator<'static>>;
// The actual `RequestItem` isn't actually both the key and the value, it's just a `Box<[u8]>`.
type RequestItem = Result<(Box<[u8]>, Box<[u8]>), Error>;
fn range(&self, range: Range<Vec<u8>>) -> Self::RequestFuture {
let mut options = ReadOptions::default();
options.set_iterate_range(range);
let iterator = self.database.iterator_opt(IteratorMode::Start, options);
let stream = stream::iter(iterator);
ready(stream)
}
}
The error I get is this:
error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
--> src/lib.rs:35:38
|
32 | fn range(&self, range: Range<Vec<u8>>) -> Self::RequestFuture {
| ----- this data with an anonymous lifetime `'_`...
...
35 | let iterator = self.database.iterator_opt(IteratorMode::Start, options);
| ------------- ^^^^^^^^^^^^
| |
| ...is used here...
...
38 | ready(stream)
| ------------- ...and is required to live as long as `'static` here
|
note: `'static` lifetime requirement introduced by the return type
--> src/lib.rs:32:47
|
32 | fn range(&self, range: Range<Vec<u8>>) -> Self::RequestFuture {
| ^^^^^^^^^^^^^^^^^^^ requirement introduced by this return type
...
38 | ready(stream)
| ------------- because of this returned expression
For more information about this error, try `rustc --explain E0759`.
I understand to mean that it can't return a 'static
reference since the anonymous lifetime on self
isn't 'static
, but shouldn't it be possible to return a 'static
Stream
from an iterator like this? The first thing I tried was to get rid of that trait bound requirement in the network protocol code, but rustc
practically dictated that the Stream
be Send + 'static
. This is because a new task gets spawned using tokio::spawn
and the trait bounds there require that the Future
is Send
and 'static
.
I've recently read Common Rust Lifetime Misconceptions, specifically this section. I understand that to mean that the Stream
returned here doesn't have to valid for the lifetime of the program, only until the network protocol code is finished writing it. Is it possible to get a Send + 'static
stream out of DBIterator
somehow? How could I go about it?