Search code examples
rustrust-sqlx

Trait is not implemented for std::string::String


I am almost sure I had this working a couple months a go, but now I came back to add some other functions to the API and it has type errors on all my sqlx calls.

  • here is the error:
 the trait `std::convert::From<std::option::Option<std::string::String>>` is not implemented for `std::string::String`
  • Here is the new function I am adding to the code:
#[post("/auth/register_usenet")]
async fn register_usenet_handler(
    req: HttpRequest,
    body: web::Json<RegisterUsenetSchema>,
    data: web::Data<AppState>,
) -> impl Responder {
    let ext = req.extensions();
    let user_id = ext.get::<uuid::Uuid>().unwrap();

    // TODO here we need to also test if usenet is true or false FOR the user making the query.
    let exists: bool = sqlx::query("SELECT EXISTS(SELECT 1 FROM users WHERE usenet_username = $1)")
        .bind(body.usenet_username.to_owned())
        .fetch_one(&data.db)
        .await
        .unwrap()
        .get(0);

    if exists {
        return HttpResponse::Conflict().json(
            serde_json::json!({"status": "fail","message": "User with that email already exists"}),
        );
    }

    let salt = SaltString::generate(&mut OsRng);
    let hashed_password = Argon2::default()
        .hash_password(body.usenet_password.as_bytes(), &salt)
        .expect("Error while hashing password")
        .to_string();
    let query_result = sqlx::query_as!(
        User,
        "UPDATE users SET usenet_username = $1, usenet_password = $2 WHERE id = $3 RETURNING *",
        body.usenet_username.to_string(),
        hashed_password,
        user_id
    )
    .fetch_one(&data.db)
    .await;

    match query_result {
        Ok(user) => {
            let user_response = serde_json::json!({"status": "success","data": serde_json::json!({
                "user": filter_user_record(&user)
            })});

            return HttpResponse::Ok().json(user_response);
        }
        Err(e) => {
            return HttpResponse::InternalServerError()
                .json(serde_json::json!({"status": "error","message": format!("{:?}", e)}));
        }
    }
}

I banging my head trying to see where my types are diff, but can't see it.

[update] cargo check output snip.

error[E0277]: the trait bound `std::string::String: std::convert::From<std::option::Option<std::string::String>>` is not satisfied
   --> src/handler.rs:103:24
    |
103 |       let query_result = sqlx::query_as!(
    |  ________________________^
104 | |         User,
105 | |         "UPDATE users SET usenet_username = $1, usenet_password = $2 WHERE id = $3 RETURNING *",
106 | |         body.usenet_username.to_string(),
107 | |         hashed_password,
108 | |         user_id
109 | |     )
    | |_____^ the trait `std::convert::From<std::option::Option<std::string::String>>` is not implemented for `std::string::String`
    |
    = help: the following other types implement trait `std::convert::From<T>`:
              <std::string::String as std::convert::From<&mut str>>
              <std::string::String as std::convert::From<&std::string::String>>
              <std::string::String as std::convert::From<&str>>
              <std::string::String as std::convert::From<Box<str>>>
              <std::string::String as std::convert::From<Cow<'a, str>>>
              <std::string::String as std::convert::From<bytestring::ByteString>>
              <std::string::String as std::convert::From<char>>
              <std::string::String as std::convert::From<url::Url>>
    = note: required for `std::option::Option<std::string::String>` to implement `Into<std::string::String>`
    = note: this error originates in the macro `$crate::sqlx_macros::expand_query` which comes from the expansion of the macro `sqlx::query_as` (in Nightly builds, run with -Z macro-backtrace for more info)

Solution

  • Ok, @drewtato's comment made me realize the issues was not on my funtions but on the User struct! Apparently on a simple look it seems ok but the documentation says that if a colum may be NULL as @drewtato pointed out I need to wrap the type in Option.

    Example:

    pub usenet_username: Option<String>,
    

    it was:

    pub usenet_username: String,