bigywater.tech
Rust · 100

spawn 与 'static

tokio::spawn(async { … name … }) 报「may outlive」是最常见的 async 编译错误。这篇从错误信息出发,验证三种修法,再说明 'static 到底限制什么、什么时候用 join! 就不用考虑它。

E0373
spawn 里的 async 块借用局部变量的错误码
3 种
修法:move、Arc 共享、clone
join!
不要求 'static 的同任务并发方式

spawn 为什么要 'static

实测 · 本机执行输出
  error[E0373]: async block may outlive the current function, but it borrows `name`, which is owned by the current function
   --> src/main.rs:4:26
    |
  4 |     let h = tokio::spawn(async { println!("hello {}", name); });
    |                          ^^^^^                        ---- `name` is borrowed here
    |                          |
    |                          may outlive borrowed value `name`
    |
    = note: async blocks are not executed immediately and must either take a reference or ownership of outside variables they use
  help: to force the async block to take ownership of `name` (and any other referenced variables), use the `move` keyword
    |
  4 |     let h = tokio::spawn(async move { println!("hello {}", name); });
    |                                ++++
  For more information about this error, try `rustc --explain E0373`.
  error: could not compile `u100a` (bin "u100a") due to 1 previous error

线程的类似限制见线程创建与 Join;调度见Tokio 任务调度。

三种修法

实测 · 本机执行输出
  编译退出码 0
  ① move 进任务,任务拥有它: tokio
  ② 3 个任务共享 Arc<Vec>:[10, 20, 30](strong_count = 1)
  ③ clone 之后原来的 name 仍然可用: tokio

join! 可以借用

实测 · 本机执行输出
  编译退出码 0
  join! 里的两个 async 块借用了 name 和 data:tokio 长度 5 / 6;name 与 data 之后仍然可用: tokio [1, 2, 3]

'static 的真实含义

实测 · 本机执行输出
  error[E0597]: `local` does not live long enough
   --> src/main.rs:7:28
    |
  7 |     let local = 5; let r = &local;
    |         -----              ^^^^^^ borrowed value does not live long enough
    |         |
    |         binding `local` declared here
  8 |     needs_static(r);
    |     --------------- argument requires that `local` is borrowed for `'static`
  9 | }
    | - `local` dropped here while still borrowed
    |
  note: requirement that the value outlives `'static` introduced here
   --> src/main.rs:1:20

任务内部的借用

实测 · 本机执行输出
  编译退出码 0
  任务内部借用自己的 v,跨 await 也没问题:10

分步演示

spawn 与 'static
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。tokio 1.x
    • 没有验证:tokio::task::LocalSet(也要求 'static)、scoped 异步任务库(如 async-scoped)、FuturesUnordered 配合借用、Box::leak 的内存泄漏对长期运行程序的影响、Send 约束的错误(见 Send 与 Sync 一篇)
    京ICP备2026067502号-1京公网安备11010502063487号