I'm having some troubles understanding the lifetime issue with the following code:
use std::future::Future;
use async_trait::async_trait;
#[derive(Debug)]
enum CalcError {
DeserializerError(serde_json::Error),
SerializerError(serde_json::Error),
DivisionByZero
}
#[derive(Debug, serde::Deserialize)]
struct CalcRequest {
a: i64,
b: i64,
}
#[derive(Debug, serde::Serialize)]
struct CalcResponse {
result: i64,
}
#[async_trait]
trait Calc {
async fn div(&self, request: &CalcRequest) -> Result<CalcResponse, CalcError>;
}
#[derive(Default)]
struct Server {}
#[async_trait]
impl Calc for Server {
async fn div(&self, request: &CalcRequest) -> Result<CalcResponse, CalcError> {
if request.b != 0 {
Ok(CalcResponse { result: request.a / request.b })
} else {
Err(CalcError::DivisionByZero)
}
}
}
#[inline]
async fn call_provider_method<'a, Service, Method, Input, Output, Fut>(
service: &Service,
f: Method,
input: &'a [u8],
) -> Result<Vec<u8>, CalcError>
where
Method: Fn(&Service, &Input) -> Fut,
Input: serde::Deserialize<'a>,
Output: serde::Serialize,
Fut: Future<Output = Result<Output, CalcError>>,
{
let request = serde_json::from_slice(input).map_err(|e| CalcError::DeserializerError(e))?;
let output = f(service, &request).await?;
let response = serde_json::to_vec(&output).map_err(|e| CalcError::SerializerError(e))?;
Ok(response)
}
#[tokio::main]
async fn main() {
let server = Server::default();
let input = "{\"a\":84,\"b\":2}";
let result = call_provider_method(&server, Calc::div, input.as_bytes()).await;
assert_eq!(result.unwrap(), "{\"result\":42}".as_bytes());
}
It gives me the following error:
error[E0631]: type mismatch in function arguments
--> src/main.rs:65:18
|
26 | async fn div(&self, request: &CalcRequest) -> Result<CalcResponse, CalcError>;
| ------------------------------------------------------------------------------ found signature of `fn(&_, &CalcRequest) -> _`
...
44 | async fn call_provider_method<'a, Service, Method, Input, Output, Fut>(
| -------------------- required by a bound in this
...
50 | Method: Fn(&Service, &Input) -> Fut,
| --------------------------- required by this bound in `call_provider_method`
...
65 | let result = call_provider_method(&server, Calc::div, input.as_bytes()).await;
| ^^^^^^^^^^^^^^^^^^^^ expected signature of `for<'r, 's> fn(&'r Server, &'s _) -> _`
error[E0271]: type mismatch resolving `for<'r, 's> <fn(&_, &CalcRequest) -> std::pin::Pin<std::boxed::Box<dyn std::future::Future<Output = std::result::Result<CalcResponse, CalcError>> + std::marker::Send>> {<_ as Calc>::div::<'_, '_, '_>} as std::ops::FnOnce<(&'r Server, &'s _)>>::Output == _`
--> src/main.rs:65:18
|
44 | async fn call_provider_method<'a, Service, Method, Input, Output, Fut>(
| -------------------- required by a bound in this
...
50 | Method: Fn(&Service, &Input) -> Fut,
| --- required by this bound in `call_provider_method`
...
65 | let result = call_provider_method(&server, Calc::div, input.as_bytes()).await;
| ^^^^^^^^^^^^^^^^^^^^ expected bound lifetime parameter, found concrete lifetime
When changing the Calc::div
function to take a CalcRequest
instead of a &CalcRequest
and removing the &
from &Service
, &Input
and &request
the code compiles:
I do however want the functions in the trait not to consume the request but accept a reference.