Rust中move || async move的变量所有权转移判定方法
Rust闭包与async块中move关键字的变量捕获规则
首先看示例代码:
use std::sync::Arc; use std::thread; pub async fn add_two_num_with_more_number(_: u64, _: &str) {} #[tokio::main] async fn main() { let many_nums: [u64; 1] = [1]; let not_copyable: Arc<str> = "foo".into(); thread::scope(|s| { many_nums.iter().for_each(|num| { let not_copyable: Arc<str> = Arc::clone(¬_copyable); s.spawn(move || async move { add_two_num_with_more_number(*num, ¬_copyable).await; }); }); }); }
以下是对两个move关键字捕获变量的判断方法和具体分析:
核心判断逻辑
每个move仅作用于它直接修饰的闭包/async块,捕获的是该块内部直接引用的、来自外部作用域的变量。判断时要结合变量的生命周期、使用方式,以及编译错误提示(比如E0373通常指向生命周期不匹配的借用变量)。
1. move-one(外层move ||的move)
- 它修饰的是传给
s.spawn的线程闭包,这个闭包负责生成后续要执行的async块。 - 闭包内部的async块用到了
*num,而num是many_nums.iter()产生的引用,生命周期仅局限于当前for_each的循环迭代。线程闭包需要在循环迭代结束后继续存在(线程执行周期更长),如果不通过move捕获num,闭包会尝试借用num,但原引用的生命周期无法覆盖线程的执行周期,触发E0373。 - 加上move-one后,线程闭包会把
num捕获进来(因为u64是Copy类型,实际是复制了值),这样async块后续执行时,*num的来源是线程闭包内部持有的值,生命周期满足要求。
2. move-two(内层async move {}的move)
- 它修饰的是async块,这个块会被线程闭包返回,由Tokio调度执行,其生命周期可能长于当前
for_each的循环迭代。 - async块内部用到了
¬_copyable,这里的not_copyable是循环内clone出来的Arc<str>,如果不加move-two,async块会尝试借用这个循环内的变量,但循环迭代结束后该变量会被销毁,导致借用生命周期不匹配,触发E0373。 - 加上move-two后,async块会把这个clone出来的
not_copyable的所有权转移进来,async块执行时,¬_copyable的来源是自身持有的Arc实例,生命周期完全满足调度需求。
内容的提问来源于stack exchange,提问作者Jim
相关产品推荐
相关产品推荐

