RUST 基于map_err的卫语句错误处理方式

RUST 基于map_err的卫语句错误处理方式

  1. 使用扩展方法

use tracing::error;
use eyre::Result;

pub trait LogErrExt<T> {
    /// 记录错误日志后返回原 Result
    /// 
    /// error!("[{}] 错误详情: {}", context, e);
    fn log_err(self, context: &str) -> Result<T>;
}

impl<T> LogErrExt<T> for Result<T> {
    fn log_err(self, context: &str) -> Result<T> {
        self.map_err(|e| {
            error!("[{}] 错误详情: {}", context, e);
            e
        })
    }
}

// 2025-07-11:
// 上面的写法在实际应用中还存在一点问题,项目中可能存在各种Result,
// 例如自定义封装的 AppResult<T>、RspResult<T>等等,
// 因此不应该只为 eyre::Result 实现 LogErrExt trait,
// 推荐以下改写

pub trait LogErrExt<T, E> {
    /// 记录错误日志后返回原 Result
    /// 
    /// error!("[{}] 错误详情: {}", context, e);
    fn log_err(self, context: &str) -> Self;
}

impl<T, E> LogErrExt<T, E> for Result<T, E> 
where
    E: std::fmt::Display
{
    fn log_err(self, context: &str) -> Self {
        self.map_err(|e| {
            tracing::error!("[{}] 错误详情: {}", context, e);
            e
        })
    }
}

// Option 同理也可以使用拓展方法,但是一般Option类型没有错误信息,所以一般直接else打印None就行
let Some(ticker) = ticker_data.get(0) else { // 直接处理else
  error!("thread A: get_ticker_price: ticker is none");
  continue;
};

// 还是记录一下Option的用法,毕竟也算符合DRY(Don't Repeat Yourself) 原则的用法
pub trait LogOptionExt<T> {
    fn log_none(self, msg: &str) -> Option<T>;
}

impl<T> LogOptionExt<T> for Option<T> {
    fn log_none(self, msg: &str) -> Option<T> {
        if self.is_none() {
            error!("[{}] 错误详情: Option is None", msg);
        }
        self
    }
}

// 使用方法
loop {

  let Some(bn) = log.block_number.log_none("log.transaction_hash") else {
    continue;
  };

}
  1. 使用卫语句, 避免了大量match写法 或者if else嵌套

tokio::spawn(async move {
        let mut interval = interval(Duration::from_secs(60));

        let lbank_client = LBankClient::new();

        loop {
            interval.tick().await;

            info!(">>>>>===== start thread A working: get fibo_usdt price =====>>>>>");

            let Ok(ticker_data) = <LBankClient as LBank>::get_ticker_price(&lbank_client).await.log_err("thread A: get_ticker_price") else {
                continue;
            };

            let Some(ticker) = ticker_data.get(0) else {
                error!("thread A: get_ticker_price: ticker is none");
                continue;
            };

            if ticker.symbol != "fibo_usdt" {
                error!("thread A: get_ticker_price: symbol != fibo_usdt");
                continue;
            };

            let Ok(price) = ticker.price.parse::<f64>() else {

                error!("thread A: get_ticker_price: price parse f64 error");
                continue;
            };

            if price == 0.0 {
                error!("thread A: get_ticker_price: price == 0");
                continue;
            }

            if let Err(_) = FiboPrice::create(ticker.price.clone()).await.log_err("thread A: FiboPrice::create") {
                continue;
            }

            info!("thread A: successful record fibo_usdt price={}", ticker.price);
            
        }
    });

posted @ 2025-03-28 23:03  等你下课啊  阅读(130)  评论(0)    收藏  举报