2

I'm trying to setup a simple GET filter, but I'm having trouble getting it to compile.

This is the function I'm trying to map to the request:

pub async fn get_users(reference_counter: Arc<RwLock<MysqlConnection>>) -> Result<impl Reply, Rejection> {
   // Get users code here
}

This is how I map the function to the GET request in main.rs:

#[tokio::main]
async fn main() {
   let db_connection = storage::establish_connection();
   let lock = RwLock::new(db_connection);
   let reference_counter = Arc::new(lock);
   let ref_filter = warp::any().map(move || reference_counter.clone());
   let get_users = warp::get()
           .and(warp::path("users"))
           .and(ref_filter.clone())
           .and_then(user::get_users);
   warp::serve(get_users).run(([127, 0, 0, 1], 3030)).await;
}

The compile error happens on and_then, it's pretty cryptic and this what it says:

error[E0599]: no method named `and_then` found for struct `warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>` in the current scope
  --> src\main.rs:21:14
   |
21 |             .and_then(user::get_users);
   |              ^^^^^^^^ method not found in `warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>`
   |
  ::: C:\Users\Yasmani\.cargo\registry\src\github.com-1ecc6299db9ec823\warp-0.2.3\src\filter\and.rs:12:1
   |
12 | pub struct And<T, U> {
   | --------------------
   | |
   | doesn't satisfy `_: warp::filter::FilterBase`
   | doesn't satisfy `_: warp::filter::Filter`
   |
   = note: the method `and_then` exists but the following trait bounds were not satisfied:
           `warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>: warp::filter::FilterBase`
           which is required by `warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>: warp::filter::Filter`
           `&warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>: warp::filter::FilterBase`
           which is required by `&warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>: warp::filter::Filter`
           `&mut warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>: warp::filter::FilterBase`
           which is required by `&mut warp::filter::and::And<warp::filter::and::And<impl warp::filter::Filter+std::marker::Copy, warp::filters::path::Exact<warp::filters::path::internal::Opaque<&str>>>, warp::filter::map::Map<impl warp::filter::Filter+std::marker::Copy, [closure@src\main.rs:17:39: 17:72 reference_counter:_]>>: warp::filter::Filter`

I believe it has something to do with the type being returned by the closure in ref_filter not matching the type expected by the function get_users, but I'm not sure why. I believe the closure returns an Arc<RwLock<MysqlConnection>>> and the get_users function takes the same. What is the problem?

Evan Carroll
  • 78,363
  • 46
  • 261
  • 468
AxiomaticNexus
  • 6,190
  • 3
  • 41
  • 61

2 Answers2

2

The problem is that the return type of your closure is not known for some reason. Looking closely at the compiler error, the return type of your closure is _. This causes the return type of the following part of your GET filter not to be Filter, which in turn means that and_then() is not implemented.

let get_users = warp::get()
        .and(warp::path("users"))
        .and(ref_filter.clone())

Solution

Annotate the return type of your closure.

let ref_filter = warp::any().map(move || -> Arc<RwLock<MysqlConnection>> {reference_counter.clone()});

(Maybe you'll find out that the type of db_connection is not what you expect.)

Context

It seems to me like the type of db_connection is really the root of your problem. I tried to create a MWE from your code by substituting an empty struct or an impl Foo for db_connection, but it compiled without any issues.

Emerson Harkin
  • 889
  • 5
  • 13
  • I tried your experiment with an empty `struct` type and got the same result; it compiles just fine. However, if I add a `MysqlConnection` type field to the `struct`, then everything breaks again. It looks like whenever you inject `MysqlConnection` anywhere, it makes the Warp APIs angry. Why? `MysqlConnection` is simply the type I'm getting from the Diesel APIs. It feels like the `struct` is implicitly deriving something (`Copy`, `Clone`, or something) when it's empty, and then stops deriving when I add the connection as a field. Any ideas? – AxiomaticNexus Jun 18 '20 at 22:41
  • 1
    I figured it out. It has something to do with `MysqlConnection` not implementing some `trait` Warp requires. The exact `trait` I don't know, but I found a different approach. I'll post a full answer on how to go about this. – AxiomaticNexus Jun 19 '20 at 00:03
  • Glad you figured it out @AxiomaticNexus! – Emerson Harkin Jun 19 '20 at 15:28
1

The issue is that MysqlConnection from the Diesel APIs does not implement some trait that the Warp APIs require. The solution is to stop using the MysqlConnection type directly here, and use a ConnectionPool that generates connections on the fly instead. ConnectionPool is part of the r2d2 feature of the Diesel package.

Now that I think about it, it makes perfect sense. Before, I was using a single database connection to process all incoming HTTP requests. This is bad, because the app could end up making multiple read operations on the same connection at the same time.

So, here is the function processing the HTTP request:

type ConnectionPool = Pool<ConnectionManager<MysqlConnection>>;
type ConnectionLock = RwLock<ConnectionPool>;
type ConnectionLockArc = Arc<ConnectionLock>;

pub async fn get_users(lock: ConnectionLockArc) -> Result<impl Reply, Rejection>{
    let users = storage::get_users(&lock);
    let response = json(&users);
    return Ok(response);
}

And here is how it's setup with the Warp APIs:

#[tokio::main]
async fn main() {
     let pool = storage::establish_connection_pool();
     let lock = RwLock::new(pool);
     let reference = Arc::new(lock);
     let resources = warp::any().map(move || reference.clone());
     let get_users = warp::get()
             .and(warp::path("users"))
             .and(resources.clone())
             .and_then(user::get_users);
     warp::serve(get_users).run(([127, 0, 0, 1], 3030)).await;
 }
AxiomaticNexus
  • 6,190
  • 3
  • 41
  • 61
  • Why are you using `RwLock` and `Arc`? – maxcountryman Jul 14 '20 at 02:17
  • @maxcountryman The app is inherently multi-threaded. `RwLock` prevents one thread from doing a read operation on the database while another is writing to it. `Arc` allows the `RwLock` to be shared across multiple threads. – AxiomaticNexus Jul 14 '20 at 15:26