Report #69499
[bug\_fix] future cannot be sent between threads safely or argument requires \`'static\` lifetime \(Tokio spawn\)
The root cause is that \`tokio::spawn\` \(and \`std::thread::spawn\`\) requires the future \(or closure\) to be \`'static\` because it may outlive the current stack frame and execute on a different thread. When an \`async fn\` takes a reference like \`&str\`, the resulting future contains that reference, tying its lifetime to the stack variable. The fix is to ensure the data is owned and moved into the task: \(1\) Convert borrowed data to an owned type before spawning. For example, change \`tokio::spawn\(process\(&s\)\)\` to first \`let s = s.to\_string\(\);\` then \`tokio::spawn\(async move \{ process\(&s\).await \}\)\` using \`async move\` to capture \`s\` by move, not by reference. \(2\) Use reference-counted smart pointers for shared immutable data: \`let data: Arc = Arc::from\(data\);\` then clone the \`Arc\` \(cheap\) and move the clone into each spawned task. \(3\) If the data is truly static \(e.g., string literals \`&'static str\`\), it can be passed directly as it meets the \`'static\` bound. Crucially, do not try to use \`std::mem::forget\` or \`Box::leak\` to force a \`'static\` lifetime unless absolutely necessary and the memory leak is intentional and managed.
Journey Context:
A developer writes an async function \`async fn download\(url: &str\) -> Vec\` and a main loop that spawns many downloads concurrently using Tokio. They write: \`let url = "https://example.com".to\_string\(\); let handle = tokio::spawn\(download\(&url\)\);\`. The compiler errors with a complex message about \`future cannot be sent between threads safely\` and \`argument requires that "url" is borrowed for 'static\`. The developer tries adding \`\+ 'static\` bounds to the \`download\` function signature, which doesn't solve the issue because the reference itself is not static. They try cloning \`url\` inside the spawn closure but forget to use \`async move\`, so the reference is still captured. They search online and find explanations about how \`tokio::spawn\` requires \`'static\` because the task may run on a different thread and the original stack frame \(containing \`url\`\) may be destroyed before the task finishes. The solution is to ensure the \`String\` is moved into the task. They refactor to \`let url = "...".to\_string\(\); tokio::spawn\(async move \{ download\(&url\).await \}\)\`, which works because \`url\` is owned by the async block, which is \`'static\`.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-20T23:08:34.142698+00:00— report_created — created