Argument requires that `'life0` must outlive `'static`?

My code as below:

use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::Mutex;
use prost::Message;
use tokio::select;
use tonic::{Code, Request, Response, Status};
use crate::interfaces::IModuleMgr;
use crate::{MODULE_MGR};
use crate::proto::test::{ CarTestCallback,};
use crate::proto::test::car_test_service_server::CarTestService;
use crate::modules::test::test_constants::constants;
use crate::interfaces::ITestBusiness;
use tokio::sync::mpsc;
use tokio_stream::Stream;
use tokio_stream::wrappers::ReceiverStream;
use tokio_util::sync::CancellationToken;

pub struct TestModuleService {
    map: Mutex<HashMap<String, u64>>,
}

type ResponseStream<T> = Pin<Box<dyn Stream<Item = Result<T, Status>> + Send>>;
impl TestModuleService {
    pub fn new() -> Self {
        Self {
            map: Mutex::new(HashMap::new()),
        }
    }

}

async fn  callback_calcel_handler<FRequest, FCancellation,T>(
                                                                     request_future: FRequest,
                                                                     cancellation_future: FCancellation,
) -> Result<Response<ResponseStream<T>>, Status>
where
    FRequest: Future<Output = Result<Response<ResponseStream<T>>, Status>> + Send + 'static,
    FCancellation:Future<Output = Result<Response<ResponseStream<T>>, Status>> + Send + 'static,
    T: Send + 'static,
{
    let token = CancellationToken::new();
    // Will call token.cancel() when the future is dropped, such as when the client cancels the request
    let _drop_guard = token.clone().drop_guard();
    let select_task = tokio::spawn(async move {
        // Can select on token cancellation on any cancellable future while handling the request,
        // allowing for custom cleanup code or monitoring
        select! {
               res = request_future => res,
               _ = token.cancelled() => cancellation_future.await,
            }
    });

    select_task.await.unwrap()
}

#[tonic::async_trait]
impl CarTestService for TestModuleService {
 
    type registerTestCallbackStream = ResponseStream<CarTestCallback>;
    async fn register_test_callback(&self, request: Request<(Empty)>)
             -> Result<Response<Self::registerTestCallbackStream>, Status> {

        let key = request.remote_addr().unwrap().to_string();
        let key_clone = key.clone();
        let request_future = async move {
            println!("register_door_callback remote_addr {:?}", key);
            let (tx, rx) = mpsc::channel(64);
            let tx_arc = Arc::new(tx);

            //todo ....
			
            let output_stream = ReceiverStream::new(rx);
            Ok(Response::new(
                Box::pin(output_stream) as Self::registerTestCallbackStream
            ))
        };
        let cancellation_future = async move {
            println!("registered from {:?} cancelled by client", key_clone);

            Err(Status::cancelled("Register cancelled by client"))
        };

        callback_calcel_handler(request_future, cancellation_future).await
    }

}

compile error:

 error[E0521]: borrowed data escapes outside of method
|    --> test_project/src/modules/test/test_module_service.rs:265:9
|     |
| 201 |     async fn register_test_callback(&self, request: Request<(Empty)>)
|     |                                     -----
|     |                                     |
|     |                                     `self` is a reference that is only valid in the method body
|     |                                     lifetime `'life0` defined here
| ...
| 265 |         test_callback_calcel_handler(request_future, cancellation_future).await
|     |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|     |         |
|     |         `self` escapes the method body here
|     |         argument requires that `'life0` must outlive `'static` 

What's wrong with my code? Why is the lifetimes of self must be 'static

this line requires the request_future and cancellation_future must be 'static:

the error message indicates either the the request_future or cancellation_future (or both) must have captured self.

are you showing the full code, or are you omitting some pieces?

I am according to the cancellation example of tonic, the links : tonic/examples/src/cancellation at master · hyperium/tonic · GitHub
The example is no compile error,but my code always failed to complie.

as I said, your code is incomplete to identify the cause of the problem.

if you are unable to reduce the problematic code into a minimal reproducible example, at least provide a link to the full code so people who are willing can examine and help, since it's highly likely that the root problem is not where you thought it was.

I edited your last post to make the link clickable, the link format was wrong. But as @nerditation said, you need to provide the code you're trying to compile, so that others can try to compile it and see the problem.

Thanks for your reply!!!
this is a minimal reproducible example :
Due to network not allow to access github, I only can pasted all code:
directory structure:
tonic-test
tonic-test/proto/
tonic-test/proto/test.proto
tonic-test/src/
tonic-test/src/proto/
tonic-test/src/proto/mod.rs
tonic-test/src/proto/test.rs //generated automaticlly
tonic-test/src/service/
tonic-test/src/service/mod.rs
tonic-test/src/service/test_service.rs
tonic-test/src/main.rs
tonic-test/build.rs
tonic-test/Cargo.toml

And the content of all files:
tonic-test/proto/test.proto

syntax = "proto3";
package test;
service TestService {
  rpc registerCallback(Empty) returns (stream TestCallback) {}
}
message Empty {}
message TestCallback {
  string name = 1;
  int32 number = 2;
}

tonic-test/build.rs

fn main() {
    tonic_build::configure()
        .build_server(true)
        .out_dir("./src/proto")
        .compile(&["proto/test.proto"], &["proto"])
        .unwrap();
}

tonic-test/Cargo.toml

[package]
name = "tonic-test"
version = "0.1.0"
edition = "2021"
homepage = "unknown"
repository = "unknown"

[dependencies]
tokio = { version = "1.36.0", features = ["full"] }
tokio-stream = "0.1.15"
tonic = "0.12.1"
tokio-vsock = "0.5.0"
vsock = "0.5.0"
tower = "0.4.13"
prost = "0.13.0"
anyhow = {version = "1.0.81"}
lazy_static = "1.4.0"
libc = "0.2.155"
tokio-util = "0.7.11"

[build-dependencies]
tonic-build = "0.12.0"

tonic-test/src/main.rs

use lazy_static::lazy_static;
use tokio::runtime::Runtime;
use tonic::transport::{Error, Server};
use crate::proto::test::test_service_server::TestServiceServer;
use crate::proto::test::test_service_server::TestService;
use crate::service::test_service::TestModuleService;

mod proto;
mod service;
const IP_PORT_ADDR : &str = "127.0.0.1:8000";
lazy_static! {

    pub static ref RUNTIME: Runtime = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
}
fn main() -> Result<(), Box<dyn std::error::Error>>{
    let addr_http = IP_PORT_ADDR.parse().unwrap();
    RUNTIME.block_on(async {
        let err = Server::builder()
            .add_service(TestServiceServer::new(TestModuleService::new()))
            .serve(addr_http)
            .await;
        if err.is_err() {
            println!("something wrong!!");
            err?
        }
        Ok(())
    })

}

tonic-test/src/service/mod.rs

pub mod test_service;

tonic-test/src/service/test_service.rs

use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use tokio::select;
use tokio::sync::mpsc;
use tokio_stream::Stream;
use tokio_stream::wrappers::ReceiverStream;
use tokio_util::sync::CancellationToken;
use tonic::{Request, Response, Status};
use crate::proto::test::{Empty, TestCallback};
use crate::proto::test::test_service_server::TestService;

pub struct TestModuleService {
    map: Mutex<HashMap<String, u64>>,
}

type ResponseStream<T> = Pin<Box<dyn Stream<Item = Result<T, Status>> + Send>>;

impl TestModuleService {
    pub fn new() -> Self {
        Self {
            map: Mutex::new(HashMap::new()),
        }
    }

}

async fn test_callback_calcel_handler<FRequest, FCancellation,T>(
    request_future: FRequest,
    cancellation_future: FCancellation,
) -> Result<Response<ResponseStream<T>>, Status>
where
    FRequest: Future<Output = Result<Response<ResponseStream<T>>, Status>> + Send + 'static,
    FCancellation:Future<Output = Result<Response<ResponseStream<T>>, Status>> + Send + 'static,
    T: Send + 'static,
{
    let token = CancellationToken::new();
    // Will call token.cancel() when the future is dropped, such as when the client cancels the request
    let _drop_guard = token.clone().drop_guard();
    let select_task = tokio::spawn(async move {
        // Can select on token cancellation on any cancellable future while handling the request,
        // allowing for custom cleanup code or monitoring
        select! {
               res = request_future => res,
               _ = token.cancelled() => cancellation_future.await,
            }
    });

    select_task.await.unwrap()
}

#[tonic::async_trait]
impl TestService for TestModuleService {
    type registerCallbackStream = ResponseStream<TestCallback>;
    async fn register_callback(&self, request: Request<Empty>) -> Result<Response<Self::registerCallbackStream>, Status> {
        let key = request.remote_addr().unwrap().to_string();
        let key_clone = key.clone();
        let request_future = async move {
            println!("register_door_callback remote_addr {:?}", key);
            let (tx, rx) = mpsc::channel(64);
            let tx_arc = Arc::new(tx);



            self.map.lock().unwrap().insert(key,1);

            let output_stream = ReceiverStream::new(rx);
            Ok(Response::new(
                Box::pin(output_stream) as Self::registerCallbackStream
            ))
        };
        let cancellation_future = async move {
            println!("registered from {:?} cancelled by client", key_clone);
            let mut map = self.map.lock().unwrap();
            let id = map.get(&key_clone).unwrap();
            map.remove(&key_clone);
            Err(Status::cancelled("Register cancelled by client"))
        };

        test_callback_calcel_handler(request_future, cancellation_future).await
    }
}

tonic-test/src/proto/mod.rs

pub mod test;

there you have it. both the request_future and cancellation_future captures self, this is the reason why self is required to be borrowed for 'static, because the futures have 'static as bounds.

it's amusing why you decided to remove these lines of code in the original post, which are the major difference between your code and the tonic example, and which are the exact cause of the compile error in the first place.

when you follow some sample code, read and understand the code, don't just copy and paste without knowing how it works!

in this particular example, the 'static bounds are not mandatory at all, because the join handle of the spawned tasks are await-ed immediately, you can get rid of tokio::spawn() entirely, which in turn eliminates the 'static bounds on the futures.

I didn't test this, but I think just removing the 'static bounds and then inline the tokio::select!() should be good, at least to solve the

"self must be borrowed for 'static"

compile error, e.g.:

async fn test_callback_calcel_handler<FRequest, FCancellation,T>(
    request_future: FRequest,
    cancellation_future: FCancellation,
) -> Result<Response<ResponseStream<T>>, Status>
where
    FRequest: Future<Output = Result<Response<ResponseStream<T>>, Status>> + Send,
    FCancellation:Future<Output = Result<Response<ResponseStream<T>>, Status>> + Send,
    T: Send,
{
    let token = CancellationToken::new();
    // Will call token.cancel() when the future is dropped, such as when the client cancels the request
    let _drop_guard = token.clone().drop_guard();
    
        // Can select on token cancellation on any cancellable future while handling the request,
        // allowing for custom cleanup code or monitoring
    select! {
           res = request_future => res,
           _ = token.cancelled() => cancellation_future.await,
    }
}

Thank you very much for your response and advice, the issue has been resolved.

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.