Zino使用一百行代码实现的错误处理 您所在的位置:网站首页 错误代码:espser3002 Zino使用一百行代码实现的错误处理

Zino使用一百行代码实现的错误处理

2023-04-14 22:04| 来源: 网络整理| 查看: 265

在Zino开发框架中,我们定义了一个通用的错误类型Error,主要目的是实现以下功能:

基于字符串将任意错误包装成同一类型;支持source,并能溯源到原始错误;支持tracing,自动记录错误信息。

这三条需求对于Zino框架至关重要,这也是为什么我们没有采用社区中的错误处理库,比如anyhow。在实际应用开发中,我们往往并不会对具体的错误类型做不同的处理,而是直接返回错误消息,所以我们采取基于字符串的错误处理:

//! Type-erased errors with tracing functionalities. use crate::SharedString; use std::{error, fmt}; mod source; pub use source::Source; /// An error type backed by an allocation-optimized string. #[derive(Debug)] pub struct Error { /// Error message. message: SharedString, /// Error source. source: Option, }

其中SharedString是Zino中用来优化静态字符串处理的类型。

/// An allocation-optimized string. pub type SharedString = std::borrow::Cow(当然,这又是一个性能和使用便利性的取舍问题)。我们的Error类型对于静态字符串的处理有巨大的性能优势,后期的bench也验证了这一点:

zino::error::Error在处理&'static str消息时只需要2ns;相比之下,anyhow::Error需要57ns

Error类型的方法实现就很直接了当:

impl Error { /// Creates a new instance with the supplied message. #[inline] pub fn new(message: impl Into) -> Self { Self { message: message.into(), source: None, } } /// Creates a new instance with the supplied message and the error source. #[inline] pub fn with_source(message: impl Into, source: impl Into) -> Self { Self { message: message.into(), source: Some(Box::new(source.into())), } } /// Wraps the error value with additional contextual message. #[inline] pub fn context(self, message: impl Into) -> Self { Self { message: message.into(), source: Some(Box::new(self)), } } /// Returns the error message. #[inline] pub fn message(&self) -> &str { self.message.as_ref() } /// Returns the error source. #[inline] pub fn source(&self) -> Option { self.source.as_deref() } /// Returns an iterator of the source errors contained by `self`. #[inline] pub fn sources(&self) -> Source { /// Next source error. next: Option Source Self { Self { next: Some(error) } } } impl { type Item = &'a Error; #[inline] fn next(&mut self) -> Option { let error = self.next?; self.next = error.source(); Some(error) } }

至此,我们实现了错误溯源的功能,并提供了.root_source()方法来追溯到原始错误。接着,我们需要将任一错误类型E: std::error::Error + 'static转换为Error类型(其中'static的生命周期限制是必须的,这是std::error::Error::source()方法的要求):

impl) -> fmt::Result { let message = &self.message; if let Some(source) = &self.source { let source = source.message(); let root_source = self.root_source().map(|err| err.message()); tracing::error!(root_source, "{message}: {source}"); write!(f, "{message}: {source}") } else { tracing::error!("{message}"); write!(f, "{message}") } } }

这里的关键就是实现std::fmt::Display。每当我们调用.to_string()时,tracing::error就会自动生成一条记录:

全部代码加起来就一百行左右:

清晰易懂,简洁优雅!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有