1

I'm trying to build a small web service in Rust using Hyper and r2d2, but I'm running into some issues concerning traits. I'm unable to parse the error message thrown by the compiler, so I was hoping someone could help me out.

Consider the following code:

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();
}

My goal is to use pool in the function hello. By using closures, I thought, I could pass an environment variable wile still living up to the expectations Hyper has. Unfortunately, I get the following error:

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

It depends on the type of pool. If I try to pass an i64 for example, it's all swell and the compiler doesn't complain.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Martijn
  • 586
  • 5
  • 19

1 Answers1

1

If we look at the source for hyper, we can see for which closures the required trait is implemented:

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)
    }
}

This means your closure needs to implement Fn(Request, Response) + Sync + Send for the Handler trait to be implemented for you. Otherwise, you need to implement it yourself. Since your closure takes pool by value, it only implements FnOnce(Request, Response) (can only be called once as pool gets moved into the function).

To fix this, take an immutable reference to the pool instead so that your function can be called multiple times (i.e. implements 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();
}

N.B. The trait Fn(Request, Response) is syntactic sugar for the higher-rank trait bound for<'r> Fn(Request<'r,'r>, Response<'r>). This is because Request and Response are both generic on a lifetime and so your function must handle Requests and Responses of any lifetime.

Djzin
  • 1,148
  • 9
  • 11