Rust actix-web: the trait `Handler<_, _>` is not implemented
Asked Answered
E

3

9

I've moved from using actix-web 3.x.x to 4.x.x. and the code that's been running perfectly fine before is now throwing this error:

the trait bound `fn(actix_web::web::Query<TweetParams>, actix_web::web::Data<Pool<Postgres>>) -> impl std::future::Future {tweets4}: Handler<_, _>` is not satisfied
  --> src/routes/all_routes.rs:74:14
   |
74 | pub async fn tweets4(
   |              ^^^^^^^ the trait `Handler<_, _>` is not implemented for `fn(actix_web::web::Query<TweetParams>, actix_web::web::Data<Pool<Postgres>>) -> impl std::future::Future {tweets4}`

After some googling it seems there is indeed a Handler trait in the actix ecosystem (however, not actix-web I think).

I can't figure out where I need to implement the trait. The error message seems to suggest that it's missing on the function itself, but my understanding is that you can only implement traits on structs and enums, not functions?

Here's the handler code:

#[get("/tweets4")]
pub async fn tweets4(
    form: web::Query<TweetParams>,
    pool: web::Data<PgPool>,
) -> Result<HttpResponse, HttpResponse> {
    let fake_json_data = r#"
    { "name": "hi" }
    "#;

    let v: Value = serde_json::from_str(fake_json_data)
        .map_err(|_| HttpResponse::InternalServerError().finish())?;

    sqlx::query!(
        r#"
        INSERT INTO users
        (id, created_at, twitter_user_id, twitter_name, twitter_handle, profile_image, profile_url, entire_user)
        VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
        "#,
        Uuid::new_v4(),
        Utc::now(),
        "3",
        "4",
        "5",
        "6",
        "7",
        v,
    )
        .execute(pool.as_ref())
        .await
        .map_err(|e| {
            println!("error is {}", e);
            HttpResponse::InternalServerError().finish()
        })?;

    Ok(HttpResponse::Ok().finish())
}

What am I getting wrong?

If helpful, the entire project is on github here.

Expressive answered 2/6, 2021 at 19:39 Comment(1)
in my case it was that Actix is expecting the handler to be an async functionNadean
E
6

After sufficient trial and error I discovered that:

  1. What the error is actually saying is that the return value is missing the necessary implementation, not the function itself (if you're a beginner like me it's not obvious from the error message...)
  2. More specifically, it seems actix didn't like the built-in HttpResponse error type and I had to replace with my own:
#[derive(Debug)]
pub struct MyError(String); // <-- needs debug and display

impl std::fmt::Display for MyError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "A validation error occured on the input.")
    }
}

impl ResponseError for MyError {} // <-- key

#[get("/tweets4")]
pub async fn tweets4(
    form: web::Query<TweetParams>,
    pool: web::Data<PgPool>,
) -> Result<HttpResponse, MyError> {
    let fake_json_data = r#"
    { "name": "hi" }
    "#;

    let v: Value = serde_json::from_str(fake_json_data).map_err(|e| {
        println!("error is {}", e);
        MyError(String::from("oh no")) // <-- here
    })?;

    sqlx::query!(
        //query
    )
        .execute(pool.as_ref())
        .await
        .map_err(|e| {
            println!("error is {}", e);
            MyError(String::from("oh no")) // <-- and here
        })?;

    Ok(HttpResponse::Ok().finish())
}

Hope helps someone in the future!

Expressive answered 3/6, 2021 at 5:33 Comment(0)
N
8

I had a very similar issue. The root cause of the problem was that I forgot to use an async function. In short, When looking at the documentation at https://actix.rs/docs/response/#json-response, make sure to use async fn index(name: web::Path<String>) -> Result<impl Responder> and not fn index(name: web::Path<String>) -> Result<impl Responder>.

Nahuatl answered 16/4, 2023 at 14:26 Comment(0)
E
6

After sufficient trial and error I discovered that:

  1. What the error is actually saying is that the return value is missing the necessary implementation, not the function itself (if you're a beginner like me it's not obvious from the error message...)
  2. More specifically, it seems actix didn't like the built-in HttpResponse error type and I had to replace with my own:
#[derive(Debug)]
pub struct MyError(String); // <-- needs debug and display

impl std::fmt::Display for MyError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "A validation error occured on the input.")
    }
}

impl ResponseError for MyError {} // <-- key

#[get("/tweets4")]
pub async fn tweets4(
    form: web::Query<TweetParams>,
    pool: web::Data<PgPool>,
) -> Result<HttpResponse, MyError> {
    let fake_json_data = r#"
    { "name": "hi" }
    "#;

    let v: Value = serde_json::from_str(fake_json_data).map_err(|e| {
        println!("error is {}", e);
        MyError(String::from("oh no")) // <-- here
    })?;

    sqlx::query!(
        //query
    )
        .execute(pool.as_ref())
        .await
        .map_err(|e| {
            println!("error is {}", e);
            MyError(String::from("oh no")) // <-- and here
        })?;

    Ok(HttpResponse::Ok().finish())
}

Hope helps someone in the future!

Expressive answered 3/6, 2021 at 5:33 Comment(0)
T
1

If you still face the same error, another solution is just to switch to 1.0.8

Tanga answered 21/5, 2022 at 17:8 Comment(3)
1.0.8 of what? Actix? But it's at v4 alreadyLawton
I think the guy took the example from an old rust book. I actually faced the same problem, the solution was just to use the version author suggests.Tanga
Sincirely that is what makes me consider Actix a unusable thing. It changes so much from version to versiont hat is nearly impossible to find consistent examples in the internet. Every single book or tutorial works only one one specific version. That is by definition a sign of a framework that you do not want to use.Ambiversion

© 2022 - 2024 — McMap. All rights reserved.