Rust异步函数传递借用参数调用时编译错误的原因与解决
异步函数传递借用参数的编译错误问题
尝试将一个异步函数传入另一个异步函数,并在内部调用时传递借用参数,代码示例如下:
use std::{future::Future, time::Duration}; #[tokio::main] async fn main() { takes_callback("foo", callback).await.expect("ERROR") } async fn callback(s: &str) -> Result<(), ()> { tokio::time::sleep(Duration::from_secs(1)).await; Ok(()) } async fn takes_callback<F, R>(s: &str, callback: F) -> Result<(), ()> where F: FnOnce(&str) -> R, R: Future<Output = Result<(), ()>>, { callback(s).await }
运行cargo check时出现如下编译错误:
$ cargo check Checking async_callback v0.1.0 (/home/andy/Desktop/async_callback) error[E0308]: mismatched types --> src/main.rs:5:5 | 5 | takes_callback("foo", callback).await.expect("ERROR") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other | = note: expected opaque type `impl for<'a> Future<Output = Result<(), ()>>` found opaque type `impl Future<Output = Result<(), ()>>` = help: consider `await`ing on both `Future`s = note: distinct uses of `impl Trait` result in different opaque types note: the lifetime requirement is introduced here --> src/main.rs:15:24 | 15 | F: FnOnce(&str) -> R, | ^ For more information about this error, try `rustc --explain E0308`. error: could not compile `async_callback` (bin "async_callback") due to previous error
移除s参数后代码可正常运行,推测问题与s在返回的Future中的生命周期有关,请求解释错误原因并提供解决办法。
错误原因
问题核心在于异步函数返回的impl Future会捕获输入参数的生命周期,但当前的泛型约束没有明确这一关联。
当定义async fn callback(s: &str) -> Result<(), ()>时,编译器会自动生成带生命周期绑定的Future类型,等价于:
fn callback<'a>(s: &'a str) -> impl Future<Output = Result<(), ()>> + 'a
这个Future的存活周期和输入的&str绑定,意味着它不能超过s的存活时间。
而takes_callback的泛型约束F: FnOnce(&str) -> R中,R仅被约束为普通Future,没有要求它和输入的&str有生命周期关联。编译器期望的是一个对任意生命周期'a都适用的Future(即impl for<'a> Future<...>),但实际传入的callback返回的Future绑定到了特定生命周期,两者不匹配,因此触发编译错误。
解决办法
需要在泛型约束中明确R的生命周期和输入&str的关联,有两种常用方式:
方式1:使用高阶生命周期(HRTB)约束
修改takes_callback的where子句,要求F对任意生命周期'a,都能接受&'a str并返回存活到'a的Future:
async fn takes_callback<F>(s: &str, callback: F) -> Result<(), ()> where F: for<'a> FnOnce(&'a str) -> impl Future<Output = Result<(), ()>> + 'a, { callback(s).await }
方式2:显式声明生命周期参数
为函数添加生命周期参数,明确R的生命周期和输入s的生命周期一致:
async fn takes_callback<'a, F, R>(s: &'a str, callback: F) -> Result<(), ()> where F: FnOnce(&'a str) -> R, R: Future<Output = Result<(), ()>> + 'a, { callback(s).await }
两种方式都能让编译器正确识别Future与输入参数的生命周期绑定关系,解决编译错误。
内容的提问来源于stack exchange,提问作者Andy Balaam
相关产品推荐
相关产品推荐

