Tokio / Tonic - How to fix this error: `self` has lifetime `'life0` but it needs to satisfy a `'static` lifetime requirement

Tokio / Tonic - How to fix this error: `self` has lifetime `'life0` but it needs to satisfy a `'static` lifetime requirement

我正在使用 Rust 和 tonic 构建 gRPC 服务器,并且 returns 流的函数存在一些问题。到目前为止,我看到的唯一例子是在函数内方便地创建 tx 和 rx 通道——但如果您需要从应用程序的其他部分接收数据,这并没有多大帮助。我有以下代码,但出现错误。

代码

use std::sync::Arc;
use std::sync::Mutex;
use futures::{Stream, StreamExt};
use tokio::sync::mpsc;
use tokio::sync::mpsc::{Sender, Receiver};
use tokio_stream::wrappers::ReceiverStream;
use tonic::transport::Server;
use tonic::{Request, Response, Status};use resourcemanager::{LineRequest, LineResponse, Position};
use resourcemanager::resource_manager_server::{ResourceManager, ResourceManagerServer};

pub mod resourcemanager {
    tonic::include_proto!("resourcemanager");
}

#[derive(Debug)]
pub struct ResourceManagerService {
    linear_rx: mpsc::Receiver<Position>,
    linear_tx: mpsc::Sender<Position>
}

#[tonic::async_trait]
impl ResourceManager for ResourceManagerService {
    async fn draw_line(&self, request: Request<LineRequest>) -> Result<Response<LineResponse>, Status> {
        Ok(Response::new(LineResponse::default()))
    }

    type StreamLinearMotorMovementStream = ReceiverStream<Result<Position, Status>>;

    async fn stream_linear_motor_movement(
        &self,
        request: Request<()>
    ) -> Result<Response<Self::StreamLinearMotorMovementStream>, Status> {
        println!("Streaming motor movements");
        let (tx, mut rx) = mpsc::channel(1);

        tokio::spawn(async move {
            while let Some(received) = self.linear_rx.recv().await {
                tx.send(Ok(received.clone())).await.unwrap();
            }
        });

       Ok(Response::new(ReceiverStream::new(rx)))
    }
}

fn main() {
    println!("Hello, world!");
}

错误

error[E0759]: `self` has lifetime `'life0` but it needs to satisfy a `'static` lifetime requirement
   --> src/main.rs:30:10
    |
30  |         &self,
    |          ^^^^ this data with lifetime `'life0`...
...
36  |         tokio::spawn(async move {
    |         ------------ ...is used and required to live as long as `'static` here
    |
note: `'static` lifetime requirement introduced by this bound
   --> /Users/xxxxxxxx/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.18.2/src/task/spawn.rs:127:28
    |
127 |         T: Future + Send + 'static,
    |                            ^^^^^^^

此错误显示在 &self 下方:

async fn stream_linear_motor_movement(
        &self,
        request: Request<()>
    )

错误消息基本上说明了一切。缩写:

async fn stream_linear_motor_movement(&self) {
    let (tx, mut rx) = mpsc::channel(1);

    tokio::spawn(async move {
        while let Some(received) = self.linear_rx.recv().await {}
    });
}

新生成的任务中的片段 self.linear_rx.recv().await 强制编译器将 self 移动到该闭包中,因此闭包可以访问 self.linear_rx。然而,由于新任务 可以 运行 永远,它要求其捕获的上下文具有 'static 的生命周期,而 &self 具有有限的,可能比 'static 短,life0 的生命周期(无论结果如何)。这意味着您无法从新生成的任务中访问 self(或从中派生的任何内容),因为无法保证在任务执行时它会存在。

你可以做的是将 ResourceManagerService 中的 linear_rx 移动到 Arc 中的 Arcstream_linear_motor_movement 中的 Arc 并移动克隆到闭包中。根据您要完成的目标,您还可以将 linear_rx 移动到 Option.take()Optionstream_linear_motor_movement 中,留下 None在它的地方。在这两种情况下,您都将拥有的对象转移到新生成的任务中,该任务的生命周期不会短于 'static。请注意 Arc 将允许多次调用 stream_linear_motor_movement,而 Option 将只允许调用它一次(因为 linear_rx 在第一次调用时被移走) .