"trait core::ops::Fn<(Request, Response)> не реализован" для закрытия при передаче пула соединений в Hyper-обработчик

Я пытаюсь создать небольшой веб-сервис в Rust, используя Hyper и r2d2, но я сталкиваюсь с некоторыми проблемами, касающимися черт. Я не могу разобрать сообщение об ошибке, выдаваемое компилятором, поэтому я надеялся, что кто-нибудь мне поможет.

Рассмотрим следующий код:

extern crate hyper;
extern crate postgres;
extern crate r2d2;
extern crate r2d2_postgres;

use hyper::Server;
use hyper::server::{Request,Response,Handler};
use r2d2_postgres::{SslMode, PostgresConnectionManager};
use r2d2::{Pool, PooledConnection};
use postgres::{Connection};

fn connect() -> Pool<PostgresConnectionManager>{
    let config = r2d2::Config::default();
    let conns = "postgres://abc:abc@localhost/abc";
    let manager = PostgresConnectionManager::new(conns, SslMode::None).unwrap();
    let pool = r2d2::Pool::new(config, manager).unwrap();
    return pool;
}

fn hello(pool: Pool<PostgresConnectionManager>, req: Request, res: Response) {
    res.send(b"Hello world").unwrap();
}

fn main() {
    let pool = connect();
    let dispatch = move |req: Request, res: Response| hello(pool, req, res);
    Server::http("127.0.0.1:3000").unwrap().handle(dispatch).unwrap();
}

Моя цель - использовать pool в функции hello, Я подумал, что используя замыкания, я мог бы передать переменную окружения, все еще соответствующую ожиданиям Hyper. К сожалению, я получаю следующую ошибку:

src/main.rs:28:45: 28:61 error: the trait `for<'r, 'r, 'r> core::ops::Fn<(hyper::server::request::Request<'r, 'r>, hyper::server::response::Response<'r>)>` is not implemented for the type `[closure@src/main.rs:27:20: 27:76 pool:r2d2::Pool<r2d2_postgres::PostgresConnectionManager>]` [E0277]
src/main.rs:28     Server::http("127.0.0.1:3000").unwrap().handle(dispatch).unwrap();
                                                           ^~~~~~~~~~~~~~~~
src/main.rs:28:45: 28:61 help: run `rustc --explain E0277` to see a detailed explanation
error: aborting due to previous error

Это зависит от типа pool, Если я попытаюсь передать i64 например, все это прекрасно, и компилятор не жалуется.

1 ответ

Решение

Если мы посмотрим на источник для гипер, мы увидим, для каких замыканий реализована требуемая черта:

impl<F> Handler for F where F: Fn(Request, Response<Fresh>), F: Sync + Send {
    fn handle<'a, 'k>(&'a self, req: Request<'a, 'k>, res: Response<'a, Fresh>) {
        self(req, res)
    }
}

Это означает, что ваше закрытие необходимо реализовать Fn(Request, Response) + Sync + Send для Handler черта, которая будет реализована для вас. В противном случае вам нужно реализовать это самостоятельно. Так как ваше закрытие занимает pool по значению, он только реализует FnOnce(Request, Response) (может быть вызван только один раз как pool перемещается в функцию).

Чтобы исправить это, вместо этого возьмите неизменную ссылку на пул, чтобы ваша функция могла вызываться несколько раз (т.е. реализует Fn(Request, Response)).

fn hello(pool: &Pool<PostgresConnectionManager>, req: Request, res: Response) {
    res.send(b"Hello world").unwrap();
}

fn main() {
    let pool = connect();
    let dispatch = move |req: Request, res: Response| hello(&pool, req, res);
    Server::http("127.0.0.1:3000").unwrap().handle(dispatch).unwrap();
}

NB Черта Fn(Request, Response) является синтаксическим сахаром для признака высшего ранга for<'r> Fn(Request<'r,'r>, Response<'r>), Это потому что Request а также Response оба являются общими на всю жизнь, и поэтому ваша функция должна обрабатывать Requestс и Responseс любой жизни.

Другие вопросы по тегам