面试题答案
一键面试1. 定义错误类型
首先,定义一个枚举来表示所有可能的错误类型,包括网络请求错误和业务逻辑错误。
use thiserror::Error;
use reqwest::Error as ReqwestError;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Network error: {0}")]
Network(#[from] ReqwestError),
#[error("Business logic error: {0}")]
Business(String),
}
这里使用了 thiserror
库来方便地定义错误类型。ReqwestError
是 reqwest
库中的网络请求错误类型,通过 #[from]
实现了从 ReqwestError
到 AppError
的自动转换。
2. 异步函数间传递错误
在异步函数中,使用 ?
操作符来传递错误。例如,假设我们有一个进行网络请求的异步函数:
use reqwest::Client;
async fn fetch_data(client: &Client, url: &str) -> Result<String, AppError> {
let response = client.get(url).send().await?;
let body = response.text().await?;
Ok(body)
}
在这个函数中,client.get(url).send().await?
和 response.text().await?
如果发生错误,会自动返回 Err(AppError::Network(_))
,因为 ReqwestError
可以自动转换为 AppError
。
3. 与Tokio错误处理机制整合
Tokio本身并没有特别的错误处理机制,但是它与Rust标准的错误处理机制无缝集成。在Tokio的异步运行时中,可以像在普通异步函数中一样处理错误。例如,在 main
函数中:
use tokio;
#[tokio::main]
async fn main() -> Result<(), AppError> {
let client = reqwest::Client::new();
let data = fetch_data(&client, "https://example.com").await?;
println!("Fetched data: {}", data);
Ok(())
}
在 main
函数中,fetch_data
返回的错误会被 ?
操作符处理,如果发生错误,main
函数会返回 Err(_)
。
4. 完整示例代码
use thiserror::Error;
use reqwest::Error as ReqwestError;
use reqwest::Client;
use tokio;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Network error: {0}")]
Network(#[from] ReqwestError),
#[error("Business logic error: {0}")]
Business(String),
}
async fn fetch_data(client: &Client, url: &str) -> Result<String, AppError> {
let response = client.get(url).send().await?;
let body = response.text().await?;
Ok(body)
}
#[tokio::main]
async fn main() -> Result<(), AppError> {
let client = reqwest::Client::new();
let data = fetch_data(&client, "https://example.com").await?;
println!("Fetched data: {}", data);
Ok(())
}
在 Cargo.toml
中添加依赖:
[dependencies]
reqwest = { version = "0.11", features = ["blocking"] }
thiserror = "1.0"
tokio = { version = "1", features = ["full"] }
通过以上步骤,我们实现了一个复杂的错误处理系统,能够在异步Rust项目中高效且正确地处理网络请求错误和业务逻辑错误,并与Tokio的运行时进行了整合。