Zino使用一百行代码实现的错误处理 | 您所在的位置:网站首页 › 错误代码:espser3002 › Zino使用一百行代码实现的错误处理 |
在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 实验室设备网 版权所有 |