为何使用async move仍无法转移两个struct的所有权?
Rust异步任务调度中的E0507错误修复
错误信息
cargo build Compiling playground v0.1.0 (/home/explorer/playground) error[E0507]: cannot move out of `config`, a captured variable in an `FnMut` closure --> src/main.rs:50:26 | 36 | pub fn execute_jobs(config: Arc<Config>) { | ------ captured outer variable ... 49 | .with_run_async(Box::new(|_, _| { | ------ captured by this `FnMut` closure 50 | Box::pin(async move { | __________________________^ 51 | | println!("run async every 2 seconds"); 52 | | 53 | | do_job(&config.first, settings).await.unwrap(); | | ------ | | | | | variable moved due to use in coroutine | | move occurs because `config` has type `Arc<Config>`, which does not implement the `Copy` trait 54 | | }) | |_________________^ `config` is moved here For more information about this error, try `rustc --explain E0507`.
settings也会触发同类错误。
问题原因
核心矛盾在于:
with_run_async接收的是FnMut类型闭包,这类闭包会被调度器多次调用(每次Cron触发时执行一次)。- 在
async move块中直接使用config和settings,会把这两个变量的所有权移动到异步块里。但变量只能被移动一次,第一次调用闭包后,原变量就被移走了,后续调用时无变量可移,编译器因此报错。
修复方案
1. 处理config(Arc类型)
Arc是原子引用计数类型,支持低成本克隆。只需在FnMut闭包内部克隆一份Arc,每次调用闭包都会生成新的引用,不会移动原变量所有权:
.with_run_async(Box::new(|_, _| { let config = config.clone(); // 克隆Arc获取新引用 Box::pin(async move { // ... 后续使用config }) }))
2. 处理settings
给Settings实现Clone trait,这样每次调用闭包时可以克隆一份实例:
#[derive(Clone)] struct Settings { first: String, second: String, }
然后在闭包内克隆settings:
.with_run_async(Box::new(|_, _| { let config = config.clone(); let settings = settings.clone(); // 克隆Settings实例 Box::pin(async move { // ... 后续使用settings }) }))
完整修复代码
use std::{sync::Arc, time::Duration}; use tokio_cron_scheduler::{JobBuilder, JobScheduler}; struct Config { first: String, second: String, } #[derive(Clone)] struct Settings { first: String, second: String, } #[tokio::main] async fn main() { let config = Config { first: "first".to_string(), second: "second".to_string(), }; execute_jobs(Arc::new(config)); // 模拟后台服务运行 tokio::time::sleep(Duration::from_secs(120)).await; } pub fn execute_jobs(config: Arc<Config>) { tokio::task::spawn(async move { let settings = Settings { first: "first".to_string(), second: "second".to_string(), }; let scheduler = JobScheduler::new().await.unwrap(); let job = JobBuilder::new() .with_cron_job_type() .with_schedule("*/2 * * * * *") .unwrap() .with_run_async(Box::new(|_, _| { let config = config.clone(); let settings = settings.clone(); Box::pin(async move { println!("每2秒执行一次异步任务"); do_job(&config.first, settings).await.unwrap(); }) })) .build() .unwrap(); scheduler.add(job).await.unwrap(); scheduler.start().await.unwrap(); }); } async fn do_job(first_config: &str, settings: Settings) -> Result<(), ()> { dbg!(first_config); dbg!(settings.second); Ok(()) }
内容的提问来源于stack exchange,提问作者Fred Hors
相关产品推荐
相关产品推荐

