Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`for<'r, 'r, 'r> ...` cannot be sent between threads safely

Tags:

rust

hyper

I learn Rust and I tried to build a micro route system built on top of hyper (it's just for learning purpose, I know frameworks exists).

I don't know how to share a "complex" type with an hyper::server::Handler. I read the error message, but unfortunately, I don't understand how to fix it (most of time, the rust compiler just says what to fix, right now I'm not sure to understand).

Here is a (non-)working over-simplified example of what I tried:

extern crate hyper;
use std::sync::Mutex;

use hyper::*;

type Route = (method::Method, String, Box<Fn(server::Request, server::Response)>);

struct MyHandler {
    routes: Mutex<Vec<Route>>
}

impl server::Handler for MyHandler {
    fn handle(&self, req: server::Request, mut res: server::Response) {
        // This is not important
    }
}

fn main() {
    // This is not important
}

And the error is:

error: the trait bound `for<'r, 'r, 'r> std::ops::Fn(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>) + 'static: std::marker::Send` is not satisfied [--explain E0277]
  --> src/main.rs:12:10
   |>
12 |>     impl server::Handler for MyHandler {
   |>          ^^^^^^^^^^^^^^^
note: `for<'r, 'r, 'r> std::ops::Fn(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>) + 'static` cannot be sent between threads safely
note: required because it appears within the type `Box<for<'r, 'r, 'r> std::ops::Fn(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>) + 'static>`
note: required because it appears within the type `(hyper::method::Method, std::string::String, Box<for<'r, 'r, 'r> std::ops::Fn(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>) + 'static>)`
note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<(hyper::method::Method, std::string::String, Box<for<'r, 'r, 'r> std::ops::Fn(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>) + 'static>)>`
note: required because it appears within the type `alloc::raw_vec::RawVec<(hyper::method::Method, std::string::String, Box<for<'r, 'r, 'r> std::ops::Fn(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>) + 'static>)>`
note: required because it appears within the type `std::vec::Vec<(hyper::method::Method, std::string::String, Box<for<'r, 'r, 'r> std::ops::Fn(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>) + 'static>)>`
note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<std::vec::Vec<(hyper::method::Method, std::string::String, Box<for<'r, 'r, 'r> std::ops::Fn(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>) + 'static>)>>`
note: required because it appears within the type `MyHandler`
note: required by `hyper::server::Handler`

It works if I use a simple integer, but not with the Route type.

So, there is a problem with the trait and something about "cannot be sent between threads safely". Reading hyper doc, I added a Mutex, but I must be dumb, I have no idea what I am doing, not sure if I should just stop to learn Rust, or keep trying.

like image 580
rap-2-h Avatar asked Aug 25 '16 09:08

rap-2-h


1 Answers

You're nearly there.

The error is saying that MyHandler doesn't implement the Send trait, which means that a type can be sent safely to other threads:

note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Mutex<std::vec::Vec<(hyper::method::Method, std::string::String, Box<for<'r, 'r, 'r> std::ops::Fn(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>) + 'static>)>>`
note: required because it appears within the type `MyHandler`
note: required by `hyper::server::Handler`

The error does point to the right place, but it's a bit overwhelming. The first line is:

note: `for<'r, 'r, 'r> std::ops::Fn(hyper::server::Request<'r, 'r>, hyper::server::Response<'r>) + 'static` cannot be sent between threads safely

Which is saying that a Fn type doesn't implement Send. This is the one in your Route type:

type Route = (method::Method, String, Box<Fn(server::Request, server::Response)>);

A Fn closure is only Send if all of the variables it captures are also Send, but here we don't know whether any closure passed in will be suitable.

The solution is simple: constrain the Fn type to be Send:

type Route = (method::Method, String, Box<Fn(server::Request, server::Response)+Send>);
like image 199
Chris Emerson Avatar answered Oct 31 '22 05:10

Chris Emerson