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

Rust异步Future await时触发panic终止程序,如何避免该问题?

问题原因说明

你之前的方案不生效有两个核心原因:

  1. 你定义的call_data返回值是()(空类型),并非Result类型,所以匹配Ok/Err的逻辑本身就不成立,也不可能捕获到错误。
  2. unwrap()方法在遇到空值/错误时会直接触发panic,而非返回Err,所以哪怕你把call_data改成返回Result,只要内部用了unwrap,错误会直接在函数内部触发panic,不会走到外层的匹配逻辑。

解决方案

方案1:从根源避免panic,用错误返回代替unwrap(最推荐)

调整call_data的返回值为Result类型,把unwrap替换为错误返回逻辑,从根源上消灭panic触发点:

use std::error::Error;
use tokio::{task, time::{interval, Duration}};

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let forever = task::spawn(async {
        let mut interval = interval(Duration::from_secs(60));
        println!("Start");
        loop {
            interval.tick().await;
            // 匹配返回的Result
            if let Err(e) = call_data("BTC-USD", "test3").await {
                eprintln!("调用call_data出错: {}", e);
                continue;
            }
            // 正常执行后续逻辑
        }
    });
    forever.await?;
    Ok(())
}

// 修改返回值为Result,错误类型可根据实际场景替换为自定义错误
async fn call_data(name:&str, table: &str) -> Result<(), Box<dyn Error>> {
    // 把unwrap换成?,出错时直接返回Err
    let data = data()?;
    // 原有数据处理逻辑
    Ok(())
}

// 假设原data函数返回Result,此处为模拟定义
fn data() -> Result<(), Box<dyn Error>> {
    // 原有业务逻辑
    Ok(())
}

方案2:捕获现有panic(适合无法修改call_data内部实现的场景)

如果没办法修改call_data内部的unwrap逻辑,可以用std::panic::catch_unwind捕获异步调用过程中的panic,避免整个程序退出:

use std::panic::{catch_unwind, AssertUnwindSafe};
use tokio::{task, time::{interval, Duration}};

#[tokio::main]
async fn main() {
    let forever = task::spawn(async {
        let mut interval = interval(Duration::from_secs(60));
        println!("Start");
        loop {
            interval.tick().await;
            // 用catch_unwind捕获panic
            let res = catch_unwind(AssertUnwindSafe(async {
                call_data("BTC-USD", "test3").await;
            }));
            if res.is_err() {
                eprintln!("call_data触发panic,跳过本次循环");
                continue;
            }
            // 正常执行后续逻辑
        }
    });
    let _ = forever.await;
}

// 未修改内部实现的原有call_data
async fn call_data(name:&str, table: &str){
    data().unwrap();
}

方案3:通过独立子task隔离panic

Tokio中单独spawn的子task如果panic,不会直接终止整个程序,只会让对应的JoinHandle返回错误,你也可以每次调用都单独spawn一个子task来隔离风险:

use tokio::{task, time::{interval, Duration}};

#[tokio::main]
async fn main() {
    let forever = task::spawn(async {
        let mut interval = interval(Duration::from_secs(60));
        println!("Start");
        loop {
            interval.tick().await;
            // 单独spawn子task执行call_data
            let task_res = task::spawn(async {
                call_data("BTC-USD", "test3").await;
            }).await;
            if task_res.is_err() {
                eprintln!("call_data执行出错,跳过本次循环");
                continue;
            }
            // 正常执行后续逻辑
        }
    });
    let _ = forever.await;
}

// 未修改内部实现的原有call_data
async fn call_data(name:&str, table: &str){
    data().unwrap();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 14:06:08