rustrust-rocket

rust rocket response implement shows that lifetime parameter `'r` due to conflicting requirements


I am learning rust now. Today I facing a lifetime issue. I want to implement an api response in rust rocket, I want the http response to use my custom entity called ApiResponse . I will build it to replace the default rocket Response. this is my code:

use rocket::serde::Deserialize;
use rocket::serde::Serialize;
use rocket::response::Responder;
use rocket::{Request, Response};
use rocket::http::{Status, ContentType};

#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
#[allow(non_snake_case)]
pub struct ApiResponse<T> {
    pub body: T,
    pub statusCode: String,
    pub resultCode: String
}

impl<'r,T> Responder<'r,'r> for ApiResponse<T> {
    fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
        Response::build_from(self.respond_to(req).unwrap())
            .header(ContentType::JSON)
            .ok()
    }
}

impl<T> Default for ApiResponse<T> where T : Default{
    fn default() -> Self {
        ApiResponse{
            body: T::default(),
            statusCode: "200".to_string(),
            resultCode: "200".to_string()
        }
    }
}

when I compile this code, shows error like this:

$ cargo build                                                                                                                                        ‹ruby-2.7.2›
   Compiling reddwarf_music v0.1.0 (/Users/dolphin/Documents/GitHub/reddwarf_music)
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'r` due to conflicting requirements
  --> src/biz/music/../../model/response/api_response.rs:17:35
   |
17 |         Response::build_from(self.respond_to(req).unwrap())
   |                                   ^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 16:31...
  --> src/biz/music/../../model/response/api_response.rs:16:31
   |
16 |     fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
   |                               ^^^^^^^
note: ...so that the type `rocket::Request<'_>` is not borrowed for too long
  --> src/biz/music/../../model/response/api_response.rs:17:46
   |
17 |         Response::build_from(self.respond_to(req).unwrap())
   |                                              ^^^
note: but, the lifetime must be valid for the lifetime `'r` as defined on the impl at 15:6...
  --> src/biz/music/../../model/response/api_response.rs:15:6
   |
15 | impl<'r,T> Responder<'r,'r> for ApiResponse<T> {
   |      ^^
note: ...so that the expression is assignable
  --> src/biz/music/../../model/response/api_response.rs:17:9
   |
17 | /         Response::build_from(self.respond_to(req).unwrap())
18 | |             .header(ContentType::JSON)
19 | |             .ok()
   | |_________________^
   = note: expected `Result<rocket::Response<'r>, _>`
              found `Result<rocket::Response<'_>, _>`

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'r` due to conflicting requirements
  --> src/biz/user/../../model/response/api_response.rs:17:35
   |
17 |         Response::build_from(self.respond_to(req).unwrap())
   |                                   ^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime defined on the method body at 16:31...
  --> src/biz/user/../../model/response/api_response.rs:16:31
   |
16 |     fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
   |                               ^^^^^^^
note: ...so that the type `rocket::Request<'_>` is not borrowed for too long
  --> src/biz/user/../../model/response/api_response.rs:17:46
   |
17 |         Response::build_from(self.respond_to(req).unwrap())
   |                                              ^^^
note: but, the lifetime must be valid for the lifetime `'r` as defined on the impl at 15:6...
  --> src/biz/user/../../model/response/api_response.rs:15:6
   |
15 | impl<'r,T> Responder<'r,'r> for ApiResponse<T> {
   |      ^^
note: ...so that the expression is assignable
  --> src/biz/user/../../model/response/api_response.rs:17:9
   |
17 | /         Response::build_from(self.respond_to(req).unwrap())
18 | |             .header(ContentType::JSON)
19 | |             .ok()
   | |_________________^
   = note: expected `Result<rocket::Response<'r>, _>`
              found `Result<rocket::Response<'_>, _>`

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0495`.
error: could not compile `reddwarf_music`
(base) 

I read the lifetime manual and tried to understand this issue. Sorry it seems too complex that I could not figure out where is my code going wrong. the compiler tell the detail message but I still have no clue to fix this problem. So why would this happen? what should I do to fix this issue? what should I learn from this issue? I have tried to build the default response:

let response = Response::new();        
Response::build_from(response).header(ContentType::JSON).ok()

it works. But when I build from my own ApiResponse:

 Response::build_from(self.respond_to(req).unwrap())
            .header(ContentType::JSON)
            .ok()

not works.


Solution

  • you have more documentation at https://api.rocket.rs/master/rocket/response/trait.Responder.html

    This is trait signature

    pub trait Responder<'r, 'o: 'r> {
        fn respond_to(self, request: &'r Request<'_>) -> Result<'o>;
    }
    

    And documentation say

    // If the response contains no borrowed data.
    impl<'r> Responder<'r, 'static> for A {
        fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> {
            todo!()
        }
    }
    
    // If the response borrows from the request.
    impl<'r> Responder<'r, 'r> for B<'r> {
        fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
            todo!()
        }
    }
    
    // If the response is or wraps a borrow that may outlive the request.
    impl<'r, 'o: 'r> Responder<'r, 'o> for &'o C {
        fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
            todo!()
        }
    }
    
    // If the response wraps an existing responder.
    impl<'r, 'o: 'r, R: Responder<'r, 'o>> Responder<'r, 'o> for D<R> {
        fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
            todo!()
        }
    }
    

    In your case it could be

    impl<'r, T> Responder<'r,'static> for ApiResponse<T> {
        fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
            todo!()
        }
    }
    

    Now, in general the implementation of Responder doesn't make sense in your case. This implementation is for making requests and ApiResponse obviously aims to store responses.

    See the String implementation

    impl<'r> Responder<'r, 'static> for String {
        fn respond_to(self, _: &'r Request<'_>) -> response::Result<'static> {
            Response::build()
                .header(ContentType::Plain)
                .sized_body(self.len(), Cursor::new(self))
                .ok()
        }
    }
    

    or doc proposition

    use std::io::Cursor;
    
    use rocket::request::Request;
    use rocket::response::{self, Response, Responder};
    use rocket::http::ContentType;
    
    struct Person {
        name: String,
        age: u16
    }
    
    impl<'r> Responder<'r, 'static> for Person {
        fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> {
            let string = format!("{}:{}", self.name, self.age);
            //--------------------------------^----------^-------
            Response::build_from(string.respond_to(req)?)
                .raw_header("X-Person-Name", self.name)
                .raw_header("X-Person-Age", self.age.to_string())
                .header(ContentType::new("application", "x-person"))
                .ok()
        }
    }