the method oneshot exists for struct Filter<ServiceFn<{closure@...}>, _>, but its trait bounds were not satisfied
主线位置:装配阶段。
AsyncFilter::layer造出的服务是同步的Filter,异步谓词进不了它的请求主线,编译器在用这个服务的地方报错。
flowchart TB l["AsyncFilter::layer(check)"] --> fl["返回 FilterLayer"] fl --> f["包出的是 Filter"] f --> need["要求 Predicate"] need --> no["只是 AsyncPredicate"] no --> err["不是 Service:E0599"]
复现
想加一个异步检查,照着 Filter::layer 的样子写 AsyncFilter::layer:
tower = { version = "=0.5.3", features = ["filter", "util"] }
tokio = { version = "=1.54.1", features = ["full"] }
use tower::filter::AsyncFilter;
use tower::{service_fn, BoxError, ServiceBuilder, ServiceExt};
async fn check(user: String) -> Result<String, BoxError> {
if user == "mallory" { Err("banned".into()) } else { Ok(user) }
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
let echo = service_fn(|s: String| async move { Ok::<_, BoxError>(s) });
let svc = ServiceBuilder::new()
.layer(AsyncFilter::layer(check))
.service(echo);
println!("{:?}", svc.oneshot("alice".to_string()).await);
}
rustc 1.99.0 的报错(中间一段指向 filter/mod.rs 第 45 行 pub struct Filter<T, U> 的源码片段省略,它标注这个 Filter 不满足 Service<_> 和 ServiceExt<_>;note: 下面还有 &Filter<...>、&mut Filter<...> 两组同样形状的约束和长类型写入文件的提示,也省略):
error[E0599]: the method `oneshot` exists for struct `Filter<ServiceFn<{closure@...}>, _>`, but its trait bounds were not satisfied
--> examples/afl_infer.rs:14:24
|
14 | println!("{:?}", svc.oneshot("alice".to_string()).await);
| ^^^^^^^ method cannot be called on `Filter<ServiceFn<{closure@...}>, _>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`tower::filter::Filter<ServiceFn<{closure@examples/afl_infer.rs:10:25: 10:36}>, fn(String) -> impl std::future::Future<Output = Result<String, Box<(dyn std::error::Error + Send + Sync + 'static)>>> {check}>: Service<_>`
which is required by `tower::filter::Filter<ServiceFn<{closure@examples/afl_infer.rs:10:25: 10:36}>, fn(String) -> impl std::future::Future<Output = Result<String, Box<(dyn std::error::Error + Send + Sync + 'static)>>> {check}>: ServiceExt<_>`
线索在类型名里:写的是 AsyncFilter::layer,报错说的却是 Filter<..>,第二个类型参数是一个返回 impl Future 的函数。
单独写一行 let _layer = AsyncFilter::layer(check);,先碰到的是另一个错误:
error[E0282]: type annotations needed
--> examples/afl_only.rs:9:16
|
9 | let _layer = AsyncFilter::layer(check);
| ^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the struct `AsyncFilter`
|
help: consider specifying the generic arguments
|
9 | let _layer = AsyncFilter::<T, _>::layer(check);
| ++++++++
layer 定义在 impl<T, U> AsyncFilter<T, U> 上,T 是内层服务的类型,layer 根本用不到它。照提示写成 AsyncFilter::<(), _>::layer(check),这一行就能编译;一旦把包出来的服务拿去用,还是上面那条 E0599(把复现程序改成 turbofish 写法跑过,报错相同)。
源码:layer 返回了同步的 FilterLayer
AsyncFilter::layer 和 Filter::layer 一字不差:
pub fn layer(predicate: U) -> FilterLayer<U> {
FilterLayer::new(predicate)
}
FilterLayer 的 Layer 实现 造出 Filter<S, U>。Filter 的 Service 实现 要求 U: Predicate<Request>,而闭包和函数只有在返回 Result 时才自动实现 Predicate;check 返回的是 future,满足的是 AsyncPredicate。Layer 本身没有任何 Service 约束(见 Layer 与 Stack),所以 .layer(...)、.service(...) 都编译通过,错误推迟到第一次把它当服务用的地方。两种 filter 的机制见 Filter。
AsyncFilter 是 #508 重写 tower::filter 时加的,第一个包含它的版本是 tower 0.4.0;那次提交里 AsyncFilter::layer 就已经返回 FilterLayer<U>。issue #597(2021 年 8 月)报告:「This seems to be a copy/paste mistake. AsyncFilter::layer returns FilterLayer, its meant to return AsyncFilterLayer.」报告人自己也在问「I wonder if fixing this would be considered a breaking change」,维护者的回复是「This is technically a breaking change, but I think it might be semver-compliant to fix it in a minor version」。
修复它的 PR #875 只改返回类型,说明里写着「This is a copy/paste slip from the original filter rework (#508)」,并把它标成破坏性变更。写这一页时(2026 年 10 月)它还没有合并。
修法
用 AsyncFilterLayer::new 或 ServiceBuilder::filter_async,两者都造出 AsyncFilter:
use tower::filter::AsyncFilterLayer;
use tower::{service_fn, BoxError, ServiceBuilder, ServiceExt};
async fn check(user: String) -> Result<String, BoxError> {
if user == "mallory" { Err("banned".into()) } else { Ok(user) }
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
let echo = service_fn(|s: String| async move { Ok::<_, BoxError>(s) });
let a = ServiceBuilder::new().layer(AsyncFilterLayer::new(check)).service(echo);
let b = ServiceBuilder::new().filter_async(check).service(echo);
for (name, svc) in [("AsyncFilterLayer::new", a), ("filter_async", b)] {
for user in ["alice", "mallory"] {
let r = svc.clone().oneshot(user.to_string()).await;
println!("{name}({user:?}) = {:?}", r.map_err(|e| e.to_string()));
}
}
}
依赖同上,M2 Max、rustc 1.99.0、--release,连跑 3 次输出相同:
AsyncFilterLayer::new("alice") = Ok("alice")
AsyncFilterLayer::new("mallory") = Err("banned")
filter_async("alice") = Ok("alice")
filter_async("mallory") = Err("banned")
a 和 b 能放进同一个数组,说明两种写法造出的是同一个类型 AsyncFilter<ServiceFn<..>, ..>。
use std::any::type_name_of_val;
use tower::filter::AsyncFilter;
use tower::{service_fn, BoxError, ServiceBuilder, ServiceExt};
#[tokio::main(flavor = "current_thread")]
async fn main() {
let echo = service_fn(|s: String| async move { Ok::<_, BoxError>(s) });
// A synchronous predicate this time.
let svc = ServiceBuilder::new()
.layer(AsyncFilter::<(), _>::layer(|s: String| Ok::<_, BoxError>(s)))
.service(echo);
println!("{}", type_name_of_val(&svc).split('<').next().unwrap());
println!("{:?}", svc.oneshot("bob".to_string()).await.map_err(|e| e.to_string()));
}
动脑筋答案
能编译。M2 Max、rustc 1.99.0、--release,连跑 3 次输出相同:
tower::filter::Filter
Ok("bob")
- 第一行:
AsyncFilter::layer返回FilterLayer,造出来的是Filter,不是AsyncFilter。 - 第二行:闭包返回
Result,正好满足同步的Predicate,Filter是一个正常的服务,请求通过。
所以确实有代码在依赖这个错误的返回类型:把同一个同步闭包交给 AsyncFilterLayer::new,用的时候就报 the method `oneshot` exists for struct `AsyncFilter<ServiceFn<{closure@...}>, {closure@...}>`, but its trait bounds were not satisfied(E0599,试过),因为同步闭包不满足 AsyncPredicate。这就是 #875 要算破坏性变更的一个具体例子。
小结
- 0.5.3 的
AsyncFilter::layer和Filter::layer完全一样,返回FilterLayer,造出同步的Filter。 - 交给它一个异步谓词,
.layer、.service都能编译,第一次当服务用时报 E0599,报错里的类型是Filter<..>;单独写成let _layer = AsyncFilter::layer(check);时报的是 E0282,因为T推不出来。 - 修法:
AsyncFilterLayer::new(pred)或ServiceBuilder::filter_async(pred)。 - 这个错从 #508(0.4.0)就在,#597 报告于 2021 年,修复 PR #875 因为改返回类型属于破坏性变更,写这一页时还没合并。