英文:
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
点,我想避免手动实现 Layer
即 Service::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` 块内部的类型
--> src/main.rs:32:64
|
32 | let response: Result<String, Infallible> = service.call(request).await;
| ^^^^^^^ 无法推断类型
|
提示: 由于存在此 `await`,该类型位于 `async` 块的一部分
--> 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'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<_>| {
// 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::<_, Infallible>(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<_>| {
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, await'ing thereby
response
}
})
})
.service_fn(|request: String| {
// Echo service
async move {
let response = request;
Ok::<_, Infallible>(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
--> src/main.rs:32:64
|
32 | let response: Result<String, Infallible> = service.call(request).await;
| ^^^^^^^ cannot infer type
|
note: the type is part of the `async` block because of this `await`
--> src/main.rs:32:85
|
32 | let response: Result<String, Infallible> = 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<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, await'ing 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, await'ing thereby
response
}
})
})
.service_fn::<Callback>(Box::new(|request: String| {
// Echo service
Box::pin(async move {
let response = request;
Ok::<_, Infallible>(response)
})
}));
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论