Rust中tokio::spawn报`one type is more general than the other`错误求助
one type is more general than the other及生命周期错误 问题场景
使用tokio::spawn时触发one type is more general than the other错误,该问题在将 trait 中的inner函数改为异步后出现;若移除AsRef<str>直接使用&str,则会出现生命周期约束不满足的错误。
初始复现代码
pub struct MyStruct {} pub trait MyTrait { async fn inner<I, S>(&self, _args: I) where I: IntoIterator<Item = S>, S: AsRef<str>, { } async fn outer(&self) { self.inner(["gud"]).await; } } impl MyTrait for MyStruct {} #[tokio::main] async fn main() { let s = MyStruct {}; tokio::spawn(async move { s.outer().await; }); }
对应错误信息
error[E0308]: mismatched types --> src/main.rs:22:5 | 22 | / tokio::spawn(async move { 23 | | s.outer().await; 24 | | }); | |______^ one type is more general than the other | = note: expected reference `&_` found reference `&_` note: the lifetime requirement is introduced here --> src/main.rs:6:25 | 6 | I: IntoIterator<Item = S>, | ^^^^^^^^
修改inner为直接用&str后的代码
async fn inner<'a, I>(&self, _args: I) where I: IntoIterator<Item = &'a str>,
新增错误信息
error: lifetime bound not satisfied --> src/main.rs:21:5 | 21 | / tokio::spawn(async move { 22 | | s.outer().await; 23 | | }); | |______^ | = note: this is a known limitation that will be removed in the future
问题原因
one type is more general than the other错误:
Rust异步函数会自动生成匿名Future类型,trait中的异步方法调用时,生成的Future会携带隐式生命周期参数。tokio::spawn要求传入的Future必须是'static(不捕获任何非静态生命周期引用),但trait中inner方法的泛型约束让生成的Future生命周期被限制得更严格,无法匹配'static要求,从而触发错误。生命周期约束不满足错误:
直接使用&'a str时,outer方法中传入的["gud"]是临时值,生命周期仅存在于outer调用作用域内,而trait方法生成的Future试图捕获该短生命周期引用。tokio::spawn要求Future为'static,当前Rust编译器对异步trait方法的处理存在已知限制,导致无法自动协调这种生命周期冲突。
解决方法
方法一:为泛型参数添加'static约束
修改trait中的inner方法,让迭代项相关类型满足'static,同时调整传入的参数为静态引用:
pub struct MyStruct {} pub trait MyTrait { async fn inner<I, S>(&self, _args: I) where I: IntoIterator<Item = S> + 'static, S: AsRef<str> + 'static, { } async fn outer(&self) { // 将字符串转为静态切片,满足'static约束 self.inner([&"gud" as &'static str]).await; } } impl MyTrait for MyStruct {} #[tokio::main] async fn main() { let s = MyStruct {}; tokio::spawn(async move { s.outer().await; }); }
方法二:使用BoxFuture统一异步返回类型
借助futures库的BoxFuture,显式指定Future的生命周期为'static,避免编译器自动推导的生命周期问题:
首先在Cargo.toml添加依赖:
futures = "0.3"
然后修改代码:
use futures::future::BoxFuture; pub struct MyStruct {} pub trait MyTrait { fn inner<I, S>(&self, _args: I) -> BoxFuture<'static, ()> where I: IntoIterator<Item = S> + 'static, S: AsRef<str> + 'static, { Box::pin(async {}) } fn outer(&self) -> BoxFuture<'static, ()> { Box::pin(async move { self.inner([&"gud" as &'static str]).await; }) } } impl MyTrait for MyStruct {} #[tokio::main] async fn main() { let s = MyStruct {}; tokio::spawn(async move { s.outer().await; }); }
方法三:使用String替代引用类型
如果业务场景允许,将迭代项改为String类型,自然满足'static约束:
pub struct MyStruct {} pub trait MyTrait { async fn inner<I>(&self, _args: I) where I: IntoIterator<Item = String>, { } async fn outer(&self) { self.inner(["gud".to_string()]).await; } } impl MyTrait for MyStruct {} #[tokio::main] async fn main() { let s = MyStruct {}; tokio::spawn(async move { s.outer().await; }); }
内容的提问来源于stack exchange,提问作者Brendan

