Tokio::spawn() cannot use self

Hey guys, I used tokio + tonic to build a simple search service. However, inside the tokio::spawn future, I cannot use the self directly.

    async fn search(&self, request: Request<Streaming<SearchRequest>>) -> Result<Response<Self::SearchStream>, Status> {
        let mut req_stream = request.into_inner();

        let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
        tokio::spawn(async move {
            while let Some(result) = req_stream.next().await {
                match result {
                    Ok(r) => {
                        if let Err(e) = self.data_store.search(&tx, r.query.as_str(), r.case_sensitive, r.with_pos) {
                            if let Err(_) = tx.send(Err(Status::internal(e.to_string()))) {
                                break;
                            }
                        }
                    }
                    Err(status) => {
                        if let Err(_) = tx.send(Err(status)) {
                            break;
                        }
                    }
                }
            }
        });

        let resp_stream = tokio_stream::wrappers::UnboundedReceiverStream::new(rx);
        Ok(Response::new(Box::pin(resp_stream)))
    }

The compiler complained:

$ cargo b
   Compiling search-service v0.1.0 (/home/foo/work/search-service)
error[E0759]: `self` has lifetime `'life0` but it needs to satisfy a `'static` lifetime requirement
   --> src/server.rs:36:22
    |
36  |     async fn search(&self, request: Request<Streaming<SearchRequest>>) -> Result<Response<Self::SearchStream>, Status> {
    |                      ^^^^ this data with lifetime `'life0`...
...
40  |         tokio::spawn(async move {
    |         ------------ ...is used and required to live as long as `'static` here
    |
note: `'static` lifetime requirement introduced by this bound
   --> /home/foo/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.1/src/task/spawn.rs:127:28
    |
127 |         T: Future + Send + 'static,
    |                            ^^^^^^^

For more information about this error, try `rustc --explain E0759`.
error: could not compile `search-service` due to previous error

I have no idea how to solve this, I'm a Rust newbie.

I've tried the first solution from rust - How to deal with tokio::spawn closure required to be 'static and &self? - Stack Overflow. However, it required me change the function signature, which seems not applicable since the search function is generated from tonic.

After checking rust - Spawning tasks with non-static lifetimes with tokio 0.1.x - Stack Overflow, I was able to fix the static lifetime issue.

diff --git a/src/server.rs b/src/server.rs
index 3180794..34a9d4e 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -14,7 +14,7 @@ mod store;
 
 #[derive(Debug)]
 struct ApiServer {
-    data_store: DataStore,
+    data_store: std::sync::Arc<DataStore>,
 }
 
@@ -37,11 +37,12 @@ impl ApiService for ApiServer {
         let mut req_stream = request.into_inner();
 
         let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
+        let data_store = self.data_store.clone();
         tokio::spawn(async move {
             while let Some(result) = req_stream.next().await {
                 match result {
                     Ok(r) => {
-                        if let Err(e) = self.data_store.search(&tx, r.query.as_str(), r.case_sensitive, r.with_pos) {
+                        if let Err(e) = data_store.search(&tx, r.query.as_str(), r.case_sensitive, r.with_pos) {
                             if let Err(_) = tx.send(Err(Status::internal(e.to_string()))) {
                                 break;
                             }
@@ -88,7 +89,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
     let args = Args::parse();
     let addr = format!("{}:{}", args.addr, args.port).parse()?;
     let server = ApiServer {
-        data_store: DataStore::new()?
+        data_store: std::sync::Arc::new(DataStore::new()?)
     };
1 Like