How to return a Service and/or resolve traits unsatisfied issues

Hi,
I am new to rust and i am trying to use tokio tower to build a Service.

My goal is to have sort of "build" method which will return a Service. The build method will allow me to stack additional tower layers as needed (within the build) and a user of the function just needs to call build() to get the service where he can do the usual ready() ... call(req).await to get the value.

Problems:
(1) tower::Service aspects a signature of the sort async fn handle(req: Request) -> Response. With a struct method - how do/can i convert a async fn handle(**&mut self**, req: Request) -> Response to that expected by tower?
(2) how to have the build function return the right Service type?
(3) i keep running into unsatisfied traits relating to Service & ServiceExt and i have no clue how to resolve these. Is there a way to approach this problem?

Thanks for any help and advice

My sample code is below:

use std::future::Future;
use std::pin::Pin;
use tower::{ServiceBuilder, Service, ServiceExt};

#[derive(Debug, Clone)]
pub struct A {
    val: String,
}

pub type Request = String;
pub type Response = String;
pub type Error = anyhow::Error;
pub type ResponseResult = Result<String, Error>;
pub type ResponseFuture = Pin<Box<dyn Future<Output = ResponseResult> + Send + 'static>>;

impl A {
    pub fn new(val: String) -> Self { Self { val } }

    // problem-1: how to convert this to `async fn call(req: Request) -> Response` expected by tower?
    pub async fn call(&mut self, req: String) -> ResponseFuture {
        let mut val = self.val.clone();

        Box::pin( async move { Ok( val + &req ) })
    }

   // how to have a fn build up a Service and return it?
    pub fn build_service(&mut self) -> impl Service<Request>  // See (Error-A) below
    {
        let mut svc = ServiceBuilder::new();
        let mut svc = svc.service_fn( move |req| {
            self.call(req);
        });

        svc
    }
}

#[tokio::main]
async fn main() {
    // tried this by commenting out build_service() above
    // and got (Error-B)
    let mut a = A{val: "hi".to_string()};

    let mut svc = ServiceBuilder::new();
    let mut svc = svc.service_fn( move |req|{
        a.call(req);
    });

    let r = svc.ready().await.unwrap().call("test".to_string()).await.unwrap();

    println!("r={}", r);

}

Errors Encountered

// ERRORS:
// ------

(Error-A)
error[E0277]: `()` is not a future
--> src/trytowerbuild.rs:26:40
|
26 |     pub fn build_service(&mut self) -> impl Service<Request> {
    |                                        ^^^^^^^^^^^^^^^^^^^^^ `()` is not a future
        |
        = help: the trait `std::future::Future` is not implemented for `()`
    = note: required because of the requirements on the impl of `Service<String>` for `ServiceFn<[closure@src/trytowerbuild.rs:28:39: 30:10]>`

// (Error-B)
error[E0599]: the method `ready` exists for struct `ServiceFn<[closure@src/trytowerbuild.rs:42:35: 44:6]>`, but its trait bounds were not satisfied
--> src/trytowerbuild.rs:46:17
|
46 |     let r = svc.ready().await.unwrap().call("test".to_string()).await.unwrap();
|                 ^^^^^ method cannot be called on `ServiceFn<[closure@src/trytowerbuild.rs:42:35: 44:6]>` due to unsatisfied trait bounds
|
::: /Users/globalflea/.cargo/registry/src/github.com-1ecc6299db9ec823/tower-0.4.8/src/util/service_fn.rs:54:1
|
54 | pub struct ServiceFn<T> {
    | -----------------------
    | |
    | doesn't satisfy `_: Service<_>`
    | doesn't satisfy `_: ServiceExt<_>`
    |
    = note: the following trait bounds were not satisfied:
    `ServiceFn<[closure@src/trytowerbuild.rs:42:35: 44:6]>: Service<_>`
    which is required by `ServiceFn<[closure@src/trytowerbuild.rs:42:35: 44:6]>: ServiceExt<_>`

You have a semicolon here after self.call(req), so that the closure doesn’t actually return the future but () instead.

This is an async fn that also has a Future as a return value. However, async fn always implicitly means that the return type is actually a future, i.e. you’ve got two levels of futures here.


With two semicolons and an async removed, it almost compiles. An additional + '_ for the impl Service<…> return type so that it’s able to capture the self argument makes your code example compile

use std::future::Future;
use std::pin::Pin;
use tower::{ServiceBuilder, Service, ServiceExt};

#[derive(Debug, Clone)]
pub struct A {
    val: String,
}

pub type Request = String;
pub type Response = String;
pub type Error = anyhow::Error;
pub type ResponseResult = Result<String, Error>;
pub type ResponseFuture = Pin<Box<dyn Future<Output = ResponseResult> + Send + 'static>>;

impl A {
    pub fn new(val: String) -> Self { Self { val } }

    // problem-1: how to convert this to `async fn call(req: Request) -> Response` expected by tower?
    pub fn call(&mut self, req: String) -> ResponseFuture {
        let mut val = self.val.clone();

        Box::pin( async move { Ok( val + &req ) })
    }

   // how to have a fn build up a Service and return it?
    pub fn build_service(&mut self) -> impl Service<Request> + '_ // See (Error-A) below
    {
        let mut svc = ServiceBuilder::new();
        let mut svc = svc.service_fn( move |req| {
            self.call(req)
        });

        svc
    }
}

#[tokio::main]
async fn main() {
    // tried this by commenting out build_service() above
    // and got (Error-B)
    let mut a = A{val: "hi".to_string()};

    let mut svc = ServiceBuilder::new();
    let mut svc = svc.service_fn( move |req|{
        a.call(req)
    });

    let r = svc.ready().await.unwrap().call("test".to_string()).await.unwrap();

    println!("r={}", r);

}
1 Like

I had lots of help when i posted this question on Discord Tokio::Tower server. The fixes are as follows:

// removed the async, otherwise i'll end up return a Future that returns a Future
pub fn call(&mut self, req: String) -> ResponseFuture  

// removed the semi-colon after the closure
let svc = svc.service_fn( move |req| {self.call(req)}); 

// added the constraint for Service to include Error=anyhow::Error - to fix Service::Error : Debug unsatisfied constrain
// added the constraint for Service to include Response=String - to allow me to debug print the results
pub fn build_service(&mut self) -> impl Service<Request, Response=String, Error=anyhow::Error>  + '_

Hi Steffahn,

Thanks for the clear explanations

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.