当前位置: 首页> 房产> 建材 > 上海网络营销软件_变态版手游_附近的教育培训机构有哪些_杭州seo软件

上海网络营销软件_变态版手游_附近的教育培训机构有哪些_杭州seo软件

时间:2025/7/12 10:28:14来源:https://blog.csdn.net/neweastsun/article/details/143417651 浏览次数:0次
上海网络营销软件_变态版手游_附近的教育培训机构有哪些_杭州seo软件

在这篇博文中,我们将探索在Rust中使用两个流行的库来简化错误处理的策略:thiserror和anyway。我们将讨论它们的特性、用例,并提供关于何时选择每个库的见解。

需求提出

让我们首先创建函数decode()来进行说明。该功能有3个步骤:

  1. 从名为input的文件中读取内容
  2. 将每行解码为base64字符串
  3. 输出打印解码后的字符串

挑战在于确定decode的返回类型,因为std::fs::read_to_string() 、base64 decode() 和String::from_utf8() 各自返回不同的错误类型。

use base64::{self, engine, Engine};fn decode() -> /* ? */ {let input = std::fs::read_to_string("input")?;for line in input.lines() {let bytes = engine::general_purpose::STANDARD.decode(line)?;println!("{}", String::from_utf8(bytes)?);}Ok(())
}

应对方法是使用trait object: Box。这是可行的,因为所有类型都实现了std::error::Error。

fn decode() -> Result<(), Box<dyn std::error::Error>> {// ...
}

虽然这在某些情况下是合适的,但它限制了调用者识别decode()中发生的实际错误的能力。然后,如果希望以不同的方式处理具体错误,则需要使用enum定义错误类型:

enum AppError {ReadError(std::io::Error),DecodeError(base64::DecodeError),StringError(std::string::FromUtf8Error),
}

通过实现std::error::Error trait,我们可以在语义上将AppError标记为错误类型。

impl std::error::Error for AppError {}

然而,这段代码无法编译,因为AppError不满足std::error::Error需要Display和Debug的约束:

error[E0277]: `AppError` doesn't implement `std::fmt::Display`
error[E0277]: `AppError` doesn't implement `Debug`

std::error::Error的定义代表了Rust中对错误类型的最低要求的共识。错误应该对用户(显示)和程序员(调试)有两种形式的描述,并且应该提供其最终错误原因。

pub trait Error: Debug + Display {fn source(&self) -> Option<&(dyn Error + 'static)> { ... }// ...
}

在实现所需的特征后,代码将是这样的:

impl std::error::Error for AppError {fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {use AppError::*;match self {ReadError(e) => Some(e),DecodeError(e) => Some(e),StringError(e) => Some(e),}}
}impl std::fmt::Display for AppError { // Error message for users.fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {use AppError::*;let message = match self {ReadError(_) => "Failed to read the file.",DecodeError(_) => "Failed to decode the input.",StringError(_) => "Failed to parse the decoded bytes.",};write!(f, "{message}")}
}impl std::fmt::Debug for AppError { // Error message for programmers.fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {writeln!(f, "{self}")?;if let Some(e) = self.source() { // <-- Use source() to retrive the root cause.writeln!(f, "\tCaused by: {e:?}")?;}Ok(())}
}

到现在可以在decode()中使用AppError:

fn decode() -> Result<(), AppError> {let input = std::fs::read_to_string("input").map_err(AppError::ReadError)?;// ...

map_err() 用于将std::io::Error转换为AppError::ReadError。使用?操作符为了更好的流程,我们可以为AppError实现From trait:

impl From<std::io::Error> for AppError {fn from(value: std::io::Error) -> Self {AppError::ReadError(value)}
}impl From<base64::DecodeError> for AppError {fn from(value: base64::DecodeError) -> Self {AppError::DecodeError(value)}
}impl From<std::string::FromUtf8Error> for AppError {fn from(value: std::string::FromUtf8Error) -> Self {AppError::StringError(value)}
}fn decode() -> Result<(), AppError> {let input = std::fs::read_to_string("input")?;for line in input.lines() {let bytes = engine::general_purpose::STANDARD.decode(line)?;println!("{}", String::from_utf8(bytes)?);}Ok(())
}fn main() {if let Err(error) = decode() {println!("{error:?}");}
}

我们做了几件事来流畅地使用自定义错误类型:

  • 实现std::error::error
  • 实现Debug和Display
  • 实现From

上面代码实现有点冗长而乏味,但幸运的是,thiserror会自动生成其中的大部分。

thiserror简化错误定义

下面使用thiserror包简化上面代码:

#[derive(thiserror::Error)]
enum AppError {#[error("Failed to read the file.")]ReadError(#[from] std::io::Error),#[error("Failed to decode the input.")]DecodeError(#[from] base64::DecodeError),#[error("Failed to parse the decoded bytes.")]StringError(#[from] std::string::FromUtf8Error),
}impl std::fmt::Debug for AppError {fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {writeln!(f, "{self}")?;if let Some(e) = self.source() {writeln!(f, "\tCaused by: {e:?}")?;}Ok(())}
}

#[error]宏生成Display, #[from]宏处理from实现source()转换std::error::error。Debug的实现仍然是提供详细的错误消息,但如果够用的话,也可以使用#derive[Debug]:

// The manual implementation of Debug
Failed to decode the input.Caused by: InvalidPadding// #[derive(Debug)]
DecodeError(InvalidPadding)

anyhow处理任何错误

在 Rust 中,anyhow是一个用于方便地处理错误的库。它提供了一种简单的方式来处理各种类型的错误,将不同的错误类型统一转换为anyhow::Error类型,使得错误处理更加灵活和简洁。anyhow构建在std::error::Error的基础上,允许在函数之间轻松地传播错误,而不需要在每个函数签名中指定具体的错误类型。

anyhow提供了简化错误处理的替代方法,类似于Box方法,下面是上面示例的再次实现:

fn decode() -> Result<(), anyhow::Error> {let input = std::fs::read_to_string("input")?;for line in input.lines() {let bytes = engine::general_purpose::STANDARD.decode(line)?;println!("{}", String::from_utf8(bytes)?);}Ok(())
}

它可以编译,因为实现std::error:: error的类型可以转换为anyway::error。错误消息如下:

Invalid padding

为了输出更多错误消息,可以使用contex():

let bytes = engine::general_purpose::STANDARD.decode(line).context("Failed to decode the input")?;

现在错误消息为:

Failed to decode the inputCaused by:Invalid padding

现在,由于anyway的类型转换和context(),我们的错误处理得到了简化。

异步编程anyhow应用

  • 在异步编程中,anyhow也可以很好地用于处理异步操作可能出现的错误。
  • 例如,一个异步函数用于从网络获取数据并进行处理,可能会遇到网络请求失败、数据解析错误等情况。
  • 以下是示例代码(假设使用tokio进行异步编程):
use anyhow::{anyhow, Result};
use tokio::io::AsyncReadExt;
use tokio::net::TcpStream;async fn get_and_process_data() -> Result<String> {let mut stream = TcpStream::connect("127.0.0.1:8080").await?;let mut buffer = [0; 1024];let n = stream.read(&mut buffer).await?;let data = String::from_utf8_lossy(&buffer[..n]);// 假设这里有一个简单的处理逻辑,可能会出错if data.is_empty() {return Err(anyhow!("Received empty data"));}Ok(data.into_owned())
}

在这个异步函数中,TcpStream::connectstream.read可能会返回错误,通过?操作符可以方便地将anyhow::Error类型的错误向上传播。

最后总结

总之,我们已经探索了thiserror 和 anyhow库的独特特性,并讨论了每个库的优点。通过选择合适的工具,Rust开发人员可以大大简化错误处理并增强代码的可维护性。

  • thiserror简化实现自定义错误类型,thiserror对于库开发来说是理想的,其中提供的宏对程序员非常友好
  • anyhow库集成任何std::error::Error,anyhow适用于内部细节不重要的应用程序,为用户提供简化的信息
关键字:上海网络营销软件_变态版手游_附近的教育培训机构有哪些_杭州seo软件

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: