Пожизненная проблема с Actix Web

Я использую промежуточное ПО с Actix-web, и у меня возникла проблема с временем жизни, которую я не мог понять.

extern crate actix_web;

use actix_web::actix::{Actor, Addr, Context, System};
use actix_web::middleware::Middleware;
use actix_web::{http, server, App, HttpRequest, Responder};
use std::collections::HashMap;

pub struct CacheActor {
    caches: HashMap<String, String>,
}

impl CacheActor {
    pub fn new() -> Self {
        CacheActor {
            caches: HashMap::new(),
        }
    }
}

impl Actor for CacheActor {
    type Context = Context<Self>;
}

fn create_resource(req: HttpRequest, addr: &Addr<CacheActor>) -> impl Responder {
    unimplemented!();
    format!("Unimplemented")
}

fn list_resources(req: HttpRequest, addr: &Addr<CacheActor>) -> impl Responder {
    unimplemented!();
    format!("Unimplemented")
}

pub trait TusMiddlewareTrait {
    fn with_tus(self, addr: &Addr<CacheActor>) -> App;
}

impl TusMiddlewareTrait for App {
    fn with_tus(self, addr: &Addr<CacheActor>) -> App {
        self.route("/files", http::Method::GET, |req| list_resources(req, addr))
            .route("/files", http::Method::POST, |req| {
                create_resource(req, addr)
            })
    }
}

fn main() {
    let system = System::new("Example");
    let cache_addr = CacheActor::new().start();

    server::new(|| App::new().with_tus(&cache_addr))
        .bind("127.0.0.1:8080")
        .unwrap()
        .run();

    system.run();
}

Я получаю следующую ошибку:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/tus/middleware.rs:84:49
   |
84 |             .route("/files", http::Method::GET, |req| list_resources(req, addr))
   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 81:5...
  --> src/tus/middleware.rs:81:5
   |
81 | /     fn with_tus(self, addr: &actix::Addr<cache::CacheActor>) -> App {
82 | |         self.middleware(TusMiddleware)
83 | |             .route("/files", http::Method::OPTIONS, tus_information)
84 | |             .route("/files", http::Method::GET, |req| list_resources(req, addr))
...  |
87 | |             })
88 | |     }
   | |_____^
   = note: ...so that the types are compatible:
           expected &&actix::address::Addr<tus::cache::CacheActor>
              found &&actix::address::Addr<tus::cache::CacheActor>
   = note: but, the lifetime must be valid for the static lifetime...

Что касается того, что я понимаю, я передаю cache_addr как ссылку на функцию with_tus. Внутри каждого закрытия в route addr также является ссылкой.

Я не понимаю, почему компилятор сказал the lifetime cannot outlive the anonymous lifetime #1. Насколько я могу судить, срок жизни cache_addr все еще переживает закрытие. Время жизни должно охватывать до system.run() строки. Может кто меня просветить?

Изменить:

Я обновил приведенный выше код до MCVE (по крайней мере, до такой степени, что достаточно просто скопировать весь код и запустить сборку груза, сохранив при этом то же сообщение об ошибке). Я не могу запустить его на ржавой площадке. На данный момент он не поддерживает actix ящик. Я попытался уменьшить его, но это дает мне другую ошибку. Извините, я новичок в Rust.

У меня двоякие вопросы, и я хотел бы понять, о чем мне говорит ошибка. Во-вторых, мне нравится знать, как правильно сделать это с actix, поэтому пример кода зависит от actix.


person Shulhi Sapli    schedule 25.11.2018    source источник


Ответы (1)


Посмотрите на App::route подпись:

pub fn route<T, F, R>(self, path: &str, method: Method, f: F) -> App<S> 
where
    F: WithFactory<T, S, R>,
    R: Responder + 'static,
    T: FromRequest<S> + 'static,

F общий зависит от T и R, которые, в свою очередь, имеют 'static требования к сроку службы.

Ваше закрытие фиксирует &Addr<CacheActor>, что оно недействительно в течение 'static времени жизни, и это вызывает ошибку.

Я вижу возможность использовать App "Состояние" непосредственно из документации:

Состояние приложения используется для всех маршрутов и ресурсов в одном приложении. При использовании http-субъекта к состоянию можно получить доступ с помощью HttpRequest :: state () как доступный только для чтения, но внутреннюю изменчивость с помощью RefCell можно использовать для достижения изменчивости состояния. Состояние также доступно для предикатов сопоставления маршрутов и промежуточного программного обеспечения.

В этом случае должно получиться что-то вроде:

extern crate actix_web;

use actix_web::actix::{Actor, Addr, Context, System};
use actix_web::{http, server, App, HttpRequest, HttpResponse, Result};
use std::collections::HashMap;
use actix_web::dev::Handler;

#[derive(Clone)]
pub struct CacheActor {
    caches: HashMap<String, String>,
}

impl CacheActor {
    pub fn new() -> Self {
        CacheActor {
            caches: HashMap::new(),
        }
    }
}

impl Actor for CacheActor {
    type Context = Context<Self>;
}

impl<S> Handler<S> for CacheActor {
    type Result = String;

    fn handle(&self, _req: &HttpRequest<S>) -> Self::Result {
        unimplemented!();
    }
}

fn list_resources(req: &HttpRequest<Addr<CacheActor>>) -> Result<HttpResponse> {
    Ok(HttpResponse::Found()
        .header(http::header::LOCATION, format!("hello {}", req.path()))
        .finish())
}

fn main() {
    let system = System::new("Example");

    server::new(|| {
        let cache_addr = CacheActor::new().start();
        App::with_state(cache_addr)
            .resource("/world", |r| r.method(http::Method::GET).f(list_resources))
    })
    .bind("127.0.0.1:8080")
    .unwrap()
    .run();

    system.run();
}
person attdona    schedule 28.11.2018