Help: consider constraining the associated type `<T as ValidateArgs<'a>>::Args` to `&'life0 S`

I am trying to write a custom extractor which will also validate the JSON body with validator crate. To validate some of the fields I need custom function with args. Here is my code -

#[tokio::main]
async fn main() {
    println!("Starting...");
    dotenv().ok();
    let uri = std::env::var("MONGODB_URI").expect("MONGODB_URI not found in .env file");
    let min_pool = std::env::var("MONGODB_MIN_POOL_SIZE").unwrap_or_default();
    let max_pool = std::env::var("MONGODB_MAX_POOL_SIZE").unwrap_or_default();
    let min_pool = min_pool.parse::<u32>().unwrap_or(5);
    let max_pool = max_pool.parse::<u32>().unwrap_or(10);
    let timeout = Duration::from_secs(5);
    // create the mongodb client options
    let mut client_options = ClientOptions::parse(uri).await.unwrap();
    client_options.max_pool_size = Some(max_pool);
    client_options.min_pool_size = Some(min_pool);
    client_options.connect_timeout = Some(timeout);
    // create the client
    let db_client = Client::with_options(client_options).unwrap();
    let app = Router::new()
        .route("/", get(handler).post(post_handler))
        .with_state(db_client);
    let addr = SocketAddr::from(([0, 0, 0, 0], 3000));
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

pub struct ValidatedBody<T>(pub T);

#[async_trait]
impl<'a, S, B, T> FromRequest<S, B> for ValidatedBody<T>
where
    B: Send + 'static,
    S: Send + Sync,
    T: ValidateArgs<'a> + 'static,
    Json<T>: FromRequest<(), B>,
{
    type Rejection = (StatusCode, &'static str);

    async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
        let Json(data) = req
            .extract::<Json<T>, _>()
            .await
            .map_err(|_| (StatusCode::BAD_REQUEST, "Invalid JSON body"))?;
        data.validate_args(state)
            .map_err(|_| (StatusCode::BAD_REQUEST, "Invalid JSON body"))?;
        Ok(Self(data))
    }
}

async fn handler() -> &'static str {
    "Hello from server"
}

#[derive(Debug, Serialize, Deserialize, Validate)]
struct MyReqBody {
    #[validate(custom(function = "validate_name", arg = "&'v_a Client"))]
    name: String,
}

fn validate_name(name: &str, db_client: &Client) -> Result<(), ValidationError> {
    Ok(())
}

async fn post_handler(
    State(client): State<Client>,
    ValidatedBody(req_body): ValidatedBody<MyReqBody>,
) -> impl IntoResponse {
    println!("{:?}", req_body);
    (StatusCode::OK, Json(serde_json::json!({"success": true})))
}

The problem is that inside from_request function I am not able to call data.validate_args with one argument.
This is the error I am getting -

error[E0308]: mismatched types
   --> src/main.rs:60:28
    |
60  |         data.validate_args(state)
    |              ------------- ^^^^^ expected associated type, found `&S`
    |              |
    |              arguments to this function are incorrect
    |
    = note: expected associated type `<T as ValidateArgs<'a>>::Args`
                     found reference `&'life0 S`
    = help: consider constraining the associated type `<T as ValidateArgs<'a>>::Args` to `&'life0 S`
    = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
note: associated function defined here
   --> /Users/nebuchadnezzar/.cargo/registry/src/github.com-1ecc6299db9ec823/validator-0.16.0/src/traits.rs:211:8
    |
211 |     fn validate_args(&self, args: Self::Args) -> Result<(), ValidationErrors>;
    |        ^^^^^^^^^^^^^

Is there any way I can achieve this?

I'm not familiar with the crate, so this is somewhat of a long shot, but does the following change help?

-impl<'a, S, B, T> FromRequest<S, B> for ValidatedBody<T>
+impl<S, B, T> FromRequest<S, B> for ValidatedBody<T>
 where
     B: Send + 'static,
     S: Send + Sync,
-    T: ValidateArgs<'a> + 'static,
+    T: 'static + for<'any> ValidateArgs<'any, Args = &'any S>,
     Json<T>: FromRequest<(), B>,

Edit: Guess I'll throw in an explanation of how I got there as it may be more generally useful, whether this works or not. I looked at the trait definition, then looking at you're code you're doing

    async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
        // ...
        data.validate_args(state)
            .map_err(|_| (StatusCode::BAD_REQUEST, "Invalid JSON body"))?;

So you need ValidateArgs::Args to be &S with the same lifetime as the state parameter:

impl<'a, S, B, T> ...
where
    T: 'static + ValidateArgs<'a, Args = &'a S>,
//                                ^^^^^^^^^^^^
{
    async fn from_request(req: Request<B>, state: &'a S) -> Result<Self, Self::Rejection> {
//                                                 ^^^

But that won't work because this is a trait implementation where you need from_request to be able to accept a state with any arbitrary lifetime (you can't change it to state: &'a S). So instead you need the higher-ranked bound I've suggested above, so ValidateArgs::Args can match state's type for any arbitrary lifetime.

Whether it works or not probably depends on how the trait is derived.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.