Wrapping error from inner future

⚓ Rust    📅 2026-01-15    👤 surdeus    👁️ 2      

surdeus

I am trying to wrap an error from a future but I cannot wrap my head around the error returned by the compiler.

I vaguely understand it is becuase of the error types are different between the Future trait and/or the Service trait. The suggestion from the compiler didn't solve the problem.

I learned the wrapping idea from this similar question but I failed to adapt it to Future trait.

use pin_project_lite::pin_project;
use std::pin::Pin;
use std::task::ready;
use std::task::{Context, Poll};
use tower::{Layer, Service};

type Response = http::Response<String>;

enum ResponseError<E> {
    Local(&'static str),
    Inner(E),
}

pin_project! {
    struct ResponseFuture<F> {
        #[pin]
        inner: F,
    }
}

impl<F, E> Future for ResponseFuture<F>
where
    F: Future<Output = Result<Response, ResponseError<E>>>,
{
    type Output = F::Output;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        let this = self.project();

        let res = ready!(this.inner.poll(cx))?;

        // I need to change the error based on some condition
        if res.status() == 300 {
            return Poll::Ready(Err(ResponseError::Local("some error")));
        }

        Poll::Ready(Ok(res))
    }
}

struct ResponseErrorService<S> {
    inner: S,
}

impl<S, Request> Service<http::Request<Request>> for ResponseErrorService<S>
where
    S: Service<http::Request<Request>, Response = Response>,
{
    type Response = S::Response;
    type Error = ResponseError<S::Error>;
    type Future = ResponseFuture<S::Future>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.inner.poll_ready(cx).map_err(ResponseError::Inner)
    }

    fn call(&mut self, request: http::Request<Request>) -> Self::Future {
        ResponseFuture {
            inner: self.inner.call(request),
        }
    }
}

struct ResponseErrorLayer;

impl<S> Layer<S> for ResponseErrorLayer {
    type Service = ResponseErrorService<S>;

    fn layer(&self, inner: S) -> Self::Service {
        Self::Service { inner }
    }
}


error[E0271]: expected `ResponseFuture<<S as Service<Request<Request>>>::Future>` to be a future that resolves to `Result<Response<String>, ResponseError<<S as Service<Request<Request>>>::Error>>`, but it resolves to `Result<Response<String>, <S as Service<Request<Request>>>::Error>`
   --> src/lib.rs:51:19
    |
 51 |     type Future = ResponseFuture<S::Future>;
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<ResponseFuture<...> as Future>::Output == Result<..., ...>`
    |
note: expected this to be `Result<http::Response<String>, ResponseError<<S as Service<http::Request<Request>>>::Error>>`
   --> src/lib.rs:25:19
    |
 25 |     type Output = F::Output;
    |                   ^^^^^^^^^
    = note: expected enum `Result<_, ResponseError<<S as Service<http::Request<Request>>>::Error>>`
               found enum `Result<_, <S as Service<http::Request<Request>>>::Error>`
note: required by a bound in `tower::Service::Future`
   --> /playground/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-service-0.3.3/src/lib.rs:319:25
    |
319 |     type Future: Future<Output = Result<Self::Response, Self::Error>>;
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Service::Future`
    = note: the full name for the type has been written to '/playground/target/debug/deps/playground-0f4c4e4ebcfac284.long-type-13754277701042178790.txt'
    = note: consider using `--verbose` to print the full type name to the console
help: consider constraining the associated type `<S as Service<http::Request<Request>>>::Error` to `ResponseError<<S as Service<http::Request<Request>>>::Error>`
    |
 47 |     S: Service<http::Request<Request>, Response = Response, Error = ResponseError<<S as Service<http::Request<Request>>>::Error>>,
    |                                                           ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

error[E0271]: expected `<S as Service<Request<Request>>>::Future` to be a future that resolves to `Result<Response<String>, ResponseError<_>>`, but it resolves to `Result<Response<String>, <S as Service<Request<Request>>>::Error>`
   --> src/lib.rs:51:19
    |
 51 |     type Future = ResponseFuture<S::Future>;
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `ResponseError<_>`, found associated type
    |
    = note: expected enum `Result<_, ResponseError<_>>`
               found enum `Result<_, <S as Service<http::Request<Request>>>::Error>`
note: required for `ResponseFuture<<S as Service<http::Request<Request>>>::Future>` to implement `std::future::Future`
   --> src/lib.rs:21:12
    |
 21 | impl<F, E> Future for ResponseFuture<F>
    |            ^^^^^^     ^^^^^^^^^^^^^^^^^
 22 | where
 23 |     F: Future<Output = Result<Response, ResponseError<E>>>,
    |               ------------------------------------------- unsatisfied trait bound introduced here
note: required by a bound in `tower::Service::Future`
   --> /playground/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-service-0.3.3/src/lib.rs:319:18
    |
319 |     type Future: Future<Output = Result<Self::Response, Self::Error>>;
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Service::Future`
help: consider constraining the associated type `<S as Service<http::Request<Request>>>::Error` to `ResponseError<_>`
    |
 47 |     S: Service<http::Request<Request>, Response = Response, Error = ResponseError<_>>,
    |                                                           ++++++++++++++++++++++++++

2 posts - 2 participants

Read full topic

🏷️ Rust_feed