Giao diện
Error Handling Architecture Robustness
Errors là thực tế của phần mềm — Rust ép bạn đối mặt với chúng thay vì trốn tránh
Rust chia errors thành 2 loại chính:
- Recoverable Errors: File không tìm thấy, network timeout →
Result<T, E> - Unrecoverable Errors: Index out of bounds, memory corruption →
panic!
2. Recoverable Errors với Result
Hầu hết lỗi là recoverable. Rust không dùng exceptions.
rust
enum Result<T, E> {
Ok(T),
Err(E),
}Xử lý với match (Verbose nhưng an toàn)
rust
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => {
panic!("Problem opening the file: {:?}", error);
}
};
}Shortcuts: unwrap và expect
Dùng khi bạn muốn crash nếu có lỗi, hoặc trong prototypes/tests.
rust
// Crash nếu Err, không có message custom
let f = File::open("hello.txt").unwrap();
// Crash nếu Err, in ra message (Recommended over unwrap)
let f = File::open("hello.txt").expect("Failed to allow open hello.txt");⚠️ IDIOMATIC RULE
Trong production code, hạn chế dùng unwrap(). Hãy dùng expect() với message rõ ràng, hoặc xử lý lỗi gracefully.
3. Propagating Errors (Toán tử ?)
Khi function không thể tự xử lý lỗi, nó "đùn đẩy" (propagate) lỗi đó cho function gọi nó.
Cách thủ công (Verbose)
rust
use std::io;
use std::io::Read;
use std::fs::File;
fn read_username_from_file() -> Result<String, io::Error> {
let f = File::open("hello.txt");
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e), // Return lỗi sớm
};
let mut s = String::new();
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(e) => Err(e), // Return lỗi
}
}Cách Idiomatic: Toán tử ?
rust
fn read_username_from_file_idiomatic() -> Result<String, io::Error> {
// Nếu open lỗi → return Err ngay lập tức
// Nếu OK → trả về file handle
let mut f = File::open("hello.txt")?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}Chaining method calls (Thậm chí còn ngắn hơn)
rust
fn read_username_ultra_short() -> Result<String, io::Error> {
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}💡 CƠ CHẾ CỦA ?
? gọi hàm from trong trait From để convert type lỗi. Ví dụ, nó có thể convert các lỗi database khác nhau về một custom error type chung của ứng dụng bạn.
1. Unrecoverable Errors với panic!
panic! in ra lỗi và clean up stack (unwinding), rồi thoát chương trình. Dùng cho bug không thể xử lý.
rust
fn main() {
// Explicit panic
panic!("crash and burn");
// Implicit panic (ví dụ truy cập ngoài mảng)
let v = vec![1, 2, 3];
v[99]; // Panics!
}Khi nào dùng panic!?
- Examples / Prototyping: Khi bạn chưa muốn lo về error handling.
- Tests: Test fail thì panic là đúng.
- Invalid State: Khi code rơi vào trạng thái "về lý thuyết không thể xảy ra" (Logic bug).
RUST_BACKTRACE
Để debug, chạy code với biến môi trường này để xem call stack:
bash
RUST_BACKTRACE=1 cargo runBest Practices: Custom Error Types
Trong ứng dụng thực tế, nên định nghĩa Error type riêng.
rust
use std::fmt;
#[derive(Debug)]
pub enum AppError {
Io(std::io::Error),
Parse(std::num::ParseIntError),
Custom(String),
}
// Implement Display để in lỗi thân thiện
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AppError::Io(err) => write!(f, "IO error: {}", err),
AppError::Parse(err) => write!(f, "Parse error: {}", err),
AppError::Custom(msg) => write!(f, "App error: {}", msg),
}
}
}
// Implement From để dùng toán tử ? tự động convert
impl From<std::io::Error> for AppError {
fn from(err: std::io::Error) -> AppError {
AppError::Io(err)
}
}
// (Tương tự cho ParseIntError...)Bảng Tóm tắt: Xử lý Lỗi
| Phương pháp | Sử dụng khi nào? |
|---|---|
Result<T, E> | Lỗi có thể xảy ra và xử lý được (network, file, parse). |
panic! | Bug, trạng thái không hợp lệ, unrecoverable state. |
unwrap() | Khi chắc chắn 100% không lỗi, hoặc demo/test. |
expect("msg") | Như unwrap nhưng cần document tại sao crash lại OK (hoặc crash message rõ ràng). |
? Operator | Dùng trong function trả về Result để propagate lỗi lên trên. |