Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Lesson 2 — Error Handling


🎯 Learning Objectives

  • Hiểu cơ chế error handling trong Rust bằng Result<T, E>.
  • Biết cách dùng match/unwrap/expect, và đặc biệt ? operator.

🔑 Key Concepts & Glossary

Result Type

Result<T, E> là một enum có hai biến thể:

  • Ok(T) đại diện cho giá trị hợp lệ.
  • Err(E) đại diện cho lỗi.

The ? Operator

expr? tương đương với:

  • Nếu exprOk(v) → trả về v.
  • Nếu exprErr(e) → return Err(e) ngay lập tức.

Error Handling Philosophy

Rust khuyến khích propagate lỗi an toàn thay vì nuốt lỗi hay throw exception. Điều này giúp code predict được control flow và compiler kiểm soát tốt hơn.

Các thuật ngữ quan trọng:

  • Result<T, E>: Enum để biểu diễn kết quả thành công hoặc lỗi.
  • ? operator: Shortcut để propagate lỗi lên caller.
  • unwrap / expect: Trích giá trị từ Result, panic nếu là Err.
  • Propagate: Truyền lỗi lên caller thay vì xử lý ngay.

💻 Example Implementation

Cargo.toml

[package]
name = "lesson02_error_handling"
version = "0.1.0"
edition = "2024"

src/main.rs


use std::fs;
use std::io;
use std::env;

fn main() -> Result<(), io::Error> {
    // Read env var with fallback
    let filename = env::var("APP_FILE").unwrap_or_else(|_| "hello.txt".to_string());

    // Use helper function that returns Result
    let content = read_file(&filename)?;
    println!("File {filename} says: {content}");

    Ok(())
}

fn read_file(path: &str) -> Result<String, io::Error> {
    let data = fs::read_to_string(path)?; // propagate error if the file does not exist
    Ok(data)
}

Nếu file hello.txt không tồn tại → chương trình trả về Err thay vì crash.


🛠️ Hands-on Exercises

  1. Basic: Viết hàm parse_number(s: &str) -> Result<i32, std::num::ParseIntError> để parse string thành số.
  2. Intermediate: Kết hợp đọc file + parse số từ file, trả về tổng của tất cả số nguyên trong đó.
  3. Challenge: Viết CLI nhỏ nhận tên file từ std::env::args(), đọc toàn bộ nội dung và in ra số ký tự. Xử lý lỗi file không tồn tại bằng ?.

🐞 Common Pitfalls & Debugging Tips

Common Pitfalls

  • Dùng .unwrap() hoặc .expect() bừa bãi → dễ panic khi lỗi.
  • Quên propagate lỗi bằng ?, khiến code rối với match.
  • Trộn lẫn nhiều loại error mà không convert (From trait).

🔄 Migration Notes (Rust 2024+)

Migration Guidance

  • ? operator đã stable từ Rust 1.13 → an toàn để dùng.
  • Nên chuyển từ try!() macro cũ sang ?.
  • Rust 2024 không có breaking change về error handling, chỉ cải thiện ergonomic khi dùng ? trong closure và async.

❓ Q&A — Common Questions

Q1. Vì sao Rust không có exceptions?

  • Vì Rust muốn control flow minh bạch: lỗi là giá trị (Result), không có jump ẩn. Nhờ đó code dễ dự đoán, hiệu năng tốt hơn, an toàn hơn.

Q2. Result<T, E> khác Option<T> thế nào?

  • Option<T>: có hoặc không, không lý do.
  • Result<T, E>: thành công hoặc lỗi kèm lý do. → Dùng Option khi “vắng mặt” là bình thường, Result khi cần thông tin lỗi.

Q3. Khi nào dùng ?, khi nào dùng match?

  • ?: khi chỉ muốn propagate lỗi nguyên trạng.
  • match: khi cần xử lý tại chỗ (retry, fallback, đổi lỗi, log thêm context).

📚 References


🌿 Wisdom Note

Đạo Đức Kinh — Chương 64

Thiên lý chi hành, thủy vu túc hạ.

A journey of a thousand miles begins beneath one’s feet.

Xử lý lỗi cũng như đi đường dài: đi từng bước nhỏ, propagate từng lỗi đúng chỗ. Không cần “nhảy vọt” (exceptions), mà giữ flow tự nhiên, an toàn.