使用async块和ServiceFn实现tower::Layer。

huangapple go评论70阅读模式
英文:

Implement tower::Layer using async block and ServiceFn

问题

Here's the translated code portion:

我正在尝试使用 [tower::layer_fn](https://docs.rs/tower/0.4.13/tower/layer/fn.layer_fn.html) 和 [tower::service_fn](https://docs.rs/tower/0.4.13/tower/fn.service_fn.html) 辅助函数来实现一个 [tower](https://docs.rs/tower/0.4.13/tower/) [Layer](https://docs.rs/tower/0.4.13/tower/trait.Layer.html)(可以编译正常):

```rust
use std::convert::Infallible;
use tower::Service;
use tower::util::ServiceFn;

tower::ServiceBuilder::new()
    .layer_fn(|mut service: ServiceFn<_>| {
        // 除了调用下游服务之外,什么都不做
        tower::service_fn(move |request| {
            service.call(request)
        })
    })
    .service_fn(|request: String| {
        // 回显服务
        async move {
            let response = request;
            Ok::<_, Infallible>(response)
        }
    });

因为在我的实际代码中有多个 .await 点,我想避免手动实现 LayerService::call(),而是在 async 块中进行。对于回显服务,这样做正常,如上所示。但是,对于 layer_fn 内部的服务,它不会编译:

tower::ServiceBuilder::new()
    .layer_fn(|mut service: ServiceFn<_>| {
        tower::service_fn(move |request| {
            // 除了调用下游服务之外,什么都不做
            async move {
                let response: Result<String, Infallible> = service.call(request).await;
                // 对响应执行一些操作,从而进行等待
                response
            }
        })
    })
    .service_fn(|request: String| {
        // 回显服务
        async move {
            let response = request;
            Ok::<_, Infallible>(response)
        }
    });
});

我收到以下错误,但不知道如何在类型方面帮助编译器:

error[E0698]: 无法推断 `async` 块内部的类型
  --&gt; src/main.rs:32:64
   |
32 |                     let response: Result<String, Infallible> = service.call(request).await;
   |                                                                ^^^^^^^ 无法推断类型
   |
提示: 由于存在此 `await`,该类型位于 `async` 块的一部分
  --&gt; src/main.rs:32:85
   |
32 |                     let response: Result<String, Infallible> = service.call(request).await;
   |                                                                                     ^^^^^^

Please note that the URLs in the code comments are still in English for reference.

<details>
<summary>英文:</summary>

I&#39;m trying to implement a [tower](https://docs.rs/tower/0.4.13/tower/) [Layer](https://docs.rs/tower/0.4.13/tower/trait.Layer.html) using the [tower::layer_fn](https://docs.rs/tower/0.4.13/tower/layer/fn.layer_fn.html) and [tower::service_fn](https://docs.rs/tower/0.4.13/tower/fn.service_fn.html) helper functions like this (compiles fine):
```lang-rust
use std::convert::Infallible;
use tower::Service;
use tower::util::ServiceFn;


tower::ServiceBuilder::new()
	.layer_fn(|mut service: ServiceFn&lt;_&gt;| {
		// Just do nothing except calling the downstream service
		tower::service_fn(move |request| {
		    service.call(request)
		})
	})
	.service_fn(|request: String| {
		// Echo service
		async move {
		    let response = request;
		    Ok::&lt;_, Infallible&gt;(response)
		}
	});

Because I have multiple .await points in my real code, I would like to avoid implementing Layer by hand, i.e. Service::call(), but instead do so in an async block. For the echo service, that does work fine, as shown above. However, for the service inside the layer_fn, that doesn't compile:

tower::ServiceBuilder::new()
	.layer_fn(|mut service: ServiceFn&lt;_&gt;| {
		tower::service_fn(move |request| {
			// Just do nothing except calling the downstream service
		    async move {
		        let response: Result&lt;String, Infallible&gt; = service.call(request).await;
		        // Do something with response, await&#39;ing thereby
		        response
		    }
		})
	})
	.service_fn(|request: String| {
        // Echo service
        async move {
            let response = request;
            Ok::&lt;_, Infallible&gt;(response)
        }
    });
});

I get the following error, but I don't know how to help the compiler with the typing:

error[E0698]: type inside `async` block must be known in this context
  --&gt; src/main.rs:32:64
   |
32 |                     let response: Result&lt;String, Infallible&gt; = service.call(request).await;
   |                                                                ^^^^^^^ cannot infer type
   |
note: the type is part of the `async` block because of this `await`
  --&gt; src/main.rs:32:85
   |
32 |                     let response: Result&lt;String, Infallible&gt; = service.call(request).await;
   |                                                                                     ^^^^^^

答案1

得分: 1

Type inference in async contexts is sometimes less powerful than in sync context. Unfortunately, the only solution I can see is to use the nightly type_alias_impl_trait:

#![feature(type_alias_impl_trait)]

type Fut = impl Future<Output = Result<String, Infallible>>;
type Callback = impl Fn(String) -> Fut;
tower::ServiceBuilder::new()
    .layer_fn(|mut service: ServiceFn<Callback>| {
        tower::service_fn(move |request| {
            // Just do nothing except calling the downstream service
            async move {
                let response: Result<String, Infallible> = service.call(request).await;
                // Do something with response, awaiting thereby
                response
            }
        })
    })
    .service_fn::<Callback>(|request: String| {
        // Echo service
        async move {
            let response = request;
            Ok::<_, Infallible>(response)
        }
    });

Or by boxing both the callback and the future:

type Fut = Pin<Box<dyn Future<Output = Result<String, Infallible>>>;
type Callback = Box<dyn Fn(String) -> Fut>;
tower::ServiceBuilder::new()
    .layer_fn(|mut service: ServiceFn<Callback>| {
        tower::service_fn(move |request| {
            // Just do nothing except calling the downstream service
            async move {
                let response: Result<String, Infallible> = service.call(request).await;
                // Do something with response, awaiting thereby
                response
            }
        })
    })
    .service_fn::<Callback>(Box::new(|request: String| {
        // Echo service
        Box::pin(async move {
            let response = request;
            Ok::<_, Infallible>(response)
        })
    });
英文:

Type inference in async contexts is sometimes less powerful than in sync context. Unfortunately, the only solution I can see is to use the nightly type_alias_impl_trait:

#![feature(type_alias_impl_trait)]

type Fut = impl Future&lt;Output = Result&lt;String, Infallible&gt;&gt;;
type Callback = impl Fn(String) -&gt; Fut;
tower::ServiceBuilder::new()
    .layer_fn(|mut service: ServiceFn&lt;Callback&gt;| {
        tower::service_fn(move |request| {
            // Just do nothing except calling the downstream service
            async move {
                let response: Result&lt;String, Infallible&gt; = service.call(request).await;
                // Do something with response, await&#39;ing thereby
                response
            }
        })
    })
    .service_fn::&lt;Callback&gt;(|request: String| {
        // Echo service
        async move {
            let response = request;
            Ok::&lt;_, Infallible&gt;(response)
        }
    });

Or by boxing both the callback and the future:

type Fut = Pin&lt;Box&lt;dyn Future&lt;Output = Result&lt;String, Infallible&gt;&gt;&gt;&gt;;
type Callback = Box&lt;dyn Fn(String) -&gt; Fut&gt;;
tower::ServiceBuilder::new()
    .layer_fn(|mut service: ServiceFn&lt;Callback&gt;| {
        tower::service_fn(move |request| {
            // Just do nothing except calling the downstream service
            async move {
                let response: Result&lt;String, Infallible&gt; = service.call(request).await;
                // Do something with response, await&#39;ing thereby
                response
            }
        })
    })
    .service_fn::&lt;Callback&gt;(Box::new(|request: String| {
        // Echo service
        Box::pin(async move {
            let response = request;
            Ok::&lt;_, Infallible&gt;(response)
        })
    }));

huangapple
  • 本文由 发表于 2023年4月11日 01:58:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/75979502.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定