Rust中如何解决返回持有局部字符串引用的Future编译错误?
问题
我尝试创建一个Vec<Future>配合try_join_all使用,代码如下:
use futures::future::try_join_all; struct Image { pub file_name: String, } struct Storage; impl Storage { async fn upload(&self, filename: &str) -> Result<String, ()> { Ok(filename.to_owned()) } } struct ImagesRepository { storage: Storage, } impl ImagesRepository { async fn upload_images(&self, images: &[Image]) -> Result<Vec<String>, ()> { let futures = images .iter() .map(|image| { let path_name = format!("somedirectory/{}", &image.file_name); let future = self.storage.upload(path_name.as_str()); future }) .collect::<Vec<_>>(); let results = try_join_all(futures).await?; Ok(results) } }
但出现编译错误:
Compiling playground v0.0.1 (/playground) error[E0515]: cannot return value referencing local variable `path_name` --> src/lib.rs:30:17 | 26 | let future = self.storage.upload(path_name.as_str()); | ------------------ `path_name` is borrowed here ... 30 | future | ^^^^^^ returns a value referencing data owned by the current function For more information about this error, try `rustc --explain E0515`. error: could not compile `playground` due to previous error
错误原因是传递给storage.upload的path_name引用仅存在于闭包内部。我知道可以将storage.upload改为接收String类型,但这会破坏其他调用该函数的代码。请问有没有办法修复这个编译错误?
解决方法
核心问题是path_name是闭包内的局部变量,当闭包返回future时,path_name会被销毁,导致future持有悬垂引用。要解决这个问题,需要让path_name的生命周期和future绑定在一起,具体可以通过async块实现:
修改map闭包的逻辑,把path_name的创建和upload调用放在同一个async块里,这样path_name会被async块捕获,其生命周期会延续到future执行完毕:
use futures::future::try_join_all; struct Image { pub file_name: String, } struct Storage; impl Storage { async fn upload(&self, filename: &str) -> Result<String, ()> { Ok(filename.to_owned()) } } struct ImagesRepository { storage: Storage, } impl ImagesRepository { async fn upload_images(&self, images: &[Image]) -> Result<Vec<String>, ()> { let futures = images .iter() .map(|image| async { let path_name = format!("somedirectory/{}", &image.file_name); self.storage.upload(path_name.as_str()).await }) .collect::<Vec<_>>(); let results = try_join_all(futures).await?; Ok(results) } }
原理说明
- 原写法中,
future是upload方法返回的未执行异步任务,它持有对path_name的引用,但path_name在闭包结束后就会被释放,导致悬垂引用。 - 使用async块后,
path_name会被包含在async块生成的future内部,async块会捕获path_name的所有权并维持其有效性直到future执行完毕,彻底解决引用生命周期不匹配的问题。
这种修改不会改变Storage::upload的原有签名,完全兼容其他调用该函数的代码。
内容的提问来源于stack exchange,提问作者Steven
相关产品推荐
相关产品推荐

