使用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
相关产品推荐
相关产品推荐

