今天聊聊rust中異常錯誤處理
1. 基礎類型:Result 和 Option,之前判斷空指針就用到過
Option<T>
-
用途:表示值可能存在(
Some(T)
)或不存在(None
),適用于無需錯誤信息的場景。fn print_list(head: Option<&Box<ListNode>>) {match head {Some(node) => {let mut current = Some(node); // 初始化當前節點指針while let Some(node) = current {print!("{} -> ", node.val);current = node.next.as_ref(); // 使用 as_ref() 獲取對 next 的引用}println!("None");}None => {println!("鏈表為空");}} }
Result<T, E>
-
用途:表示操作可能成功(
Ok(T)
)或失敗(Err(E)
),E
?為錯誤類型。fn divide(numerator: f64, denominator: f64) -> Result<f64, String> {if denominator == 0.0 {Err(String::from("Denominator cannot be zero!"))} else {Ok(numerator / denominator)} }
2.快捷方法:unwrap 和 expect?
unwrap():成功返回值,失敗則 panic。
expect(msg):類似 unwrap,但可附加錯誤信息。
let content = read_file("file.txt").unwrap(); // 危險:可能崩潰let content = read_file("file.txt").expect("讀取文件失敗");
3.錯誤傳播:? 運算符
????????簡化錯誤傳播:自動將錯誤返回給調用者,需函數返回 Result。
fn process_file() -> Result<(), std::io::Error> {let content = read_file("file.txt")?; // 錯誤時直接返回println!("Content: {}", content);Ok(())
}
4.自定義錯誤,一般接口校驗用的多
寫一個校驗錯誤有如下四個,id為空,name重名,id查詢為空,name限制30字符超長錯誤
use std::fmt;
//原生寫法
// 定義校驗錯誤枚舉
#[derive(Debug, PartialEq)]
pub enum ValidationError {IdEmpty,NameDuplicate(String), // 攜帶重復的名稱IdNotFound(String), // 攜帶未找到的IDNameTooLong(usize), // 攜帶實際長度
}// 實現錯誤描述
impl fmt::Display for ValidationError {fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {match self {ValidationError::IdEmpty => write!(f, "ID不能為空"),ValidationError::NameDuplicate(name) => write!(f, "名稱 '{}' 已存在", name),ValidationError::IdNotFound(id) => write!(f, "ID '{}' 不存在", id),ValidationError::NameTooLong(len) => write!(f, "名稱長度不能超過30字符(當前長度:{})", len),}}
}// 實現標準錯誤Trait
impl std::error::Error for ValidationError {}
使用 thiserror 簡化(推薦)
[dependencies]
thiserror = "1.0"
use thiserror::Error;#[derive(Error, Debug, PartialEq)]
pub enum ValidationError {#[error("ID不能為空")]IdEmpty,#[error("名稱 '{0}' 已存在")]NameDuplicate(String),#[error("ID '{0}' 不存在")]IdNotFound(String),#[error("名稱長度不能超過30字符(當前長度:{0})")]NameTooLong(usize),
}// 校驗用戶輸入
pub fn validate_user_input(id: &str, name: &str) -> Result<(), ValidationError> {// 檢查ID是否為空if id.trim().is_empty() {return Err(ValidationError::IdEmpty);}// 檢查名稱長度if name.len() > 30 {return Err(ValidationError::NameTooLong(name.len()));}Ok(())
}// 模擬檢查名稱是否重復
pub fn check_name_unique(name: &str) -> Result<(), ValidationError> {let existing_names = vec!["Alice", "Bob"];if existing_names.contains(&name) {return Err(ValidationError::NameDuplicate(name.to_string()));}Ok(())
}// 模擬根據ID查詢數據是否存在
pub fn find_by_id(id: &str) -> Result<(), ValidationError> {let valid_ids = vec!["1001", "1002"];if !valid_ids.contains(&id) {return Err(ValidationError::IdNotFound(id.to_string()));}Ok(())
}fn main() {let existing_names = vec![String::from("Alice"), String::from("Bob")];// 測試不同的錯誤場景match validate_user_input("", "Charlie") {Ok(_) => println!("校驗成功."),Err(e) => println!("校驗失敗: {}", e),}match check_name_unique( "Alice") {Ok(_) => println!("校驗成功."),Err(e) => println!("校驗失敗: {}", e),}match find_by_id("789") {Ok(_) => println!("校驗成功."),Err(e) => println!("校驗失敗: {}", e),}match validate_user_input("123", &"A".repeat(31)) {Ok(_) => println!("校驗成功."),Err(e) => println!("校驗失敗: {}", e),}
}
總結,rust沒有try/catch這樣的異常處理機制,而是通過Result 和 Option這樣枚舉處理,unwrap 和 expect在開發調試過程中用比較多,自定義錯誤,校驗類函數用的多。