面试题答案
一键面试Option和Result的基本概念
- Option: 用于表示值可能存在或不存在的情况。在Rust中,
Option<T>
有两个变体:Some(T)
表示存在一个值,None
表示不存在值。通常用于处理可能为空的返回值,例如在查找集合中元素时,如果找不到则返回None
。 - Result<T, E>: 用于处理可能成功或失败的操作。它有两个变体:
Ok(T)
表示操作成功并返回一个值,Err(E)
表示操作失败并返回一个错误值。E
通常是自定义的错误类型,用于详细描述错误原因。
复杂业务逻辑中的错误处理设计
文件读取
假设我们要从文件中读取数据。std::fs::read_to_string
函数返回 Result<String, std::io::Error>
。
use std::fs::read_to_string;
fn read_file(file_path: &str) -> Result<String, std::io::Error> {
read_to_string(file_path)
}
网络请求
对于网络请求,我们可以使用 reqwest
库。reqwest::get
函数返回 Result<reqwest::Response, reqwest::Error>
。
use reqwest;
async fn fetch_data(url: &str) -> Result<reqwest::Response, reqwest::Error> {
reqwest::get(url).await
}
数据解析
假设我们从文件或网络响应中获取的数据是JSON格式,我们使用 serde_json::from_str
来解析。它返回 Result<T, serde_json::Error>
。
use serde_json;
fn parse_json<T: serde::de::DeserializeOwned>(json_str: &str) -> Result<T, serde_json::Error> {
serde_json::from_str(json_str)
}
数据库存储
假设我们使用 rusqlite
库来存储数据。Connection::execute
函数返回 Result<usize, rusqlite::Error>
。
use rusqlite;
fn save_to_db(conn: &rusqlite::Connection, data: &str) -> Result<usize, rusqlite::Error> {
conn.execute("INSERT INTO your_table (data) VALUES (?1)", &[data])
}
组合操作并处理错误
为了避免错误处理代码的冗余,我们可以使用 ?
操作符。?
操作符会自动将 Result
中的错误返回,如果是 Ok
则提取其中的值。
use std::fs::read_to_string;
use reqwest;
use serde_json;
use rusqlite;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 文件读取
let file_content = read_file("path/to/file.txt")?;
// 网络请求
let response = fetch_data("https://example.com/api").await?;
let response_text = response.text().await?;
// 数据解析
let parsed_file: Vec<String> = parse_json(&file_content)?;
let parsed_response: Vec<String> = parse_json(&response_text)?;
// 数据库连接
let conn = rusqlite::Connection::open("your_database.db")?;
// 数据库存储
for data in parsed_file.into_iter().chain(parsed_response.into_iter()) {
save_to_db(&conn, &data)?;
}
Ok(())
}
确保健壮性
- 自定义错误类型: 对于复杂业务逻辑,定义自定义错误类型可以使错误处理更具可读性和可维护性。例如:
#[derive(Debug)]
enum MyAppError {
FileReadError(std::io::Error),
NetworkError(reqwest::Error),
JsonParseError(serde_json::Error),
DatabaseError(rusqlite::Error),
}
impl From<std::io::Error> for MyAppError {
fn from(e: std::io::Error) -> Self {
MyAppError::FileReadError(e)
}
}
impl From<reqwest::Error> for MyAppError {
fn from(e: reqwest::Error) -> Self {
MyAppError::NetworkError(e)
}
}
impl From<serde_json::Error> for MyAppError {
fn from(e: serde_json::Error) -> Self {
MyAppError::JsonParseError(e)
}
}
impl From<rusqlite::Error> for MyAppError {
fn from(e: rusqlite::Error) -> Self {
MyAppError::DatabaseError(e)
}
}
然后修改我们的函数签名,使其返回 Result<T, MyAppError>
。
fn read_file(file_path: &str) -> Result<String, MyAppError> {
read_to_string(file_path).map_err(Into::into)
}
- 错误日志记录: 在错误处理过程中,可以使用日志库(如
log
)记录详细的错误信息,以便于调试和排查问题。
use log::error;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
match read_file("path/to/file.txt") {
Ok(content) => println!("File read successfully: {}", content),
Err(e) => {
error!("File read error: {:?}", e);
return Err(e.into());
}
}
// 其他操作...
Ok(())
}
通过以上设计,我们可以在复杂的Rust业务逻辑中实现高效、清晰且可维护的错误处理机制,提高整个流程的健壮性。