症状
错误在一条 note 里同时点名“逃逸”与“借用”:closure may outlive the current function, but it borrows config——闭包被存进比本次调用活得久的地方(spawn、处理器表、结构体字段),而它的捕获物借用了函数返回即死的局部变量,两个生命周期端点冲突。编译器建议加 move;微妙之处在于 move 会改变调用方此后对该值的可用性——有时正确的修法是 clone 或 Arc,而不是 move。加了 move 编译过、却把错误挪到“使用已被移动的值”,是走错方向的典型信号。
诊断思路
先读 E0373 的 note,借用的变量与闭包逃逸的位置都被点名,冲突对即修复范围;再判断需要所有权还是共享——函数不再需要该值就 move,闭包与调用方都要就 clone 或 Arc;最后验证两侧同时编译通过。2021 版的分离捕获让“只借用部分字段”的闭包更常见,也更容易踩进这个错误。
根因
闭包默认按引用捕获变量,把自身生命周期绑到被借用的局部上。当闭包逃逸出函数——进线程、进注册表、进结构体字段——那些局部早已消亡而闭包还在引用它们,编译器要求要么所有权(move)、要么共享所有权(clone 进 Arc/Rc)。
修复与验证
按需三选:调用方用完了就裸 move;还要用就先 clone 一份快照进闭包;长期共享用 Arc 双持。框架处理器注册这类“闭包活得和应用一样久”的场景,把拥有捕获当作设计默认:闭包需要的一切要么 move 进来、要么 clone 进来、要么 Arc 共享,绝不从请求作用域的栈上借用。验证时逃逸闭包编译通过;测试确认原函数返回后闭包仍正确执行;调用方的后续使用照常工作。move 会改变 Drop 时机,移动点之后的代码要复查。
代码示例
error[E0373]: closure may outlive the current function,
but it borrows `config`
--> src/worker.rs:12:24
|
12 | let task = spawn(move || use(&config));
| ------- ^- `config` is borrowed here
| |
| this closure may outlive the current function
let task = thread::spawn(move || process(config)); // ownership travels
let snapshot = config.clone();
let task = thread::spawn(move || process(snapshot));
use(&config); // caller keeps its copy
let shared = Arc::new(config);
let for_task = Arc::clone(&shared);
let task = thread::spawn(move || process(for_task));
use(&*shared);