You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用tokio::spawn和Box<dyn Error>时,如何修复跨线程发送不安全错误?

编译错误分析:dyn Error 不满足Send约束

问题代码

#[tokio::main]
async fn main() {
    tokio::spawn(async {
        foo().await;
    });
}

async fn foo() {
    let f1 = bar();
    let f2 = bar();

    tokio::join!(f1, f2);
}

async fn bar() -> Result<(), Box<dyn std::error::Error>> {
    println!("Hello world");
    Ok(())
}

编译错误信息

error[E0277]: `(dyn std::error::Error + 'static)` cannot be sent between threads safely
   --> src/main.rs:5:18
    |
5   |       tokio::spawn(async {
    |  _____------------_^
    | |     |
    | |     required by a bound introduced by this call
6   | |         foo().await;
7   | |     });
    | |_____^ `(dyn std::error::Error + 'static)` cannot be sent between threads safely
    |
    = help: the trait `Send` is not implemented for `(dyn std::error::Error + 'static)`
    = note: required for `Unique<(dyn std::error::Error + 'static)>` to implement `Send`
    = note: required because it appears within the type `Box<dyn Error>`
    = note: required because it appears within the type `Result<(), Box<dyn Error>>`
    = note: required because it appears within the type `MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>`
    = note: required because it appears within the type `(MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>, MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>)`
    = note: required because it captures the following types: `ResumeTy`, `impl Future<Output = Result<(), Box<dyn Error>>>`, `(MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>, MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>)`, `&mut (MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>, MaybeDone<impl Future<Output = Result<(), Box<dyn Error>>>>)`, `u32`, `[closure@join.rs:95:17]`, `PollFn<[closure@join.rs:95:17]>`, `()`
note: required because it's used within this `async fn` body

错误原因

Tokio的tokio::spawn函数要求传入的异步任务(future)必须实现Send trait——因为Tokio的多线程运行时会在不同线程间调度任务,只有实现Send的类型才能安全跨线程传递。

你的代码里,bar返回Result<(), Box<dyn std::error::Error>>,但std::error::Error本身没有强制要求实现Send,所以Box<dyn std::error::Error>默认不保证是Send的。当foo用tokio::join!组合两个bar的future时,生成的future会包含这个非Send的错误类型,导致整个被spawn包裹的异步块不满足约束,最终触发编译错误。

移除bar的返回类型后,返回值变成()(天然实现Send),整个future满足约束,所以编译通过。

解决方法

修改bar的返回类型,给dyn Error加上Send约束,确保错误类型可以安全跨线程传递:

async fn bar() -> Result<(), Box<dyn std::error::Error + Send>> {
    println!("Hello world");
    Ok(())
}

如果场景需要,还可以进一步加上Sync约束:Box<dyn std::error::Error + Send + Sync>。

内容的提问来源于stack exchange,提问作者guenhter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 11:16:27