Cộng đồng ngôn ngữ lập trình Rust đang tham gia vào một cuộc thảo luận sôi nổi về các phương pháp xử lý lỗi, được khơi mào bởi phân tích chi tiết của nhóm iroh về hành trình chuyển đổi từ xử lý lỗi generic sang structured. Cuộc tranh luận này làm nổi bật những căng thẳng cơ bản giữa năng suất của nhà phát triển và độ chính xác của API, ảnh hưởng đến mọi dự án Rust.
Sự phân chia lớn: Anyhow vs Thiserror
Hệ sinh thái Rust phần lớn đã chia thành hai phe trong việc xử lý lỗi. Phương pháp anyhow
coi lỗi như một kiểu generic lớn có thể bao bọc bất cứ thứ gì, giúp triển khai nhanh chóng và xuất sắc trong việc debug với đầy đủ backtrace. Ở phía bên kia, thiserror
tạo ra các enum variant được thiết kế cẩn thận cho mọi trường hợp lỗi có thể xảy ra, cung cấp cho người dùng các kiểu lỗi chính xác mà họ có thể match và xử lý khác nhau.
Các thành viên cộng đồng đang đặt câu hỏi liệu sự phức tạp này có đáng giá hay không. Một số nhà phát triển bày tỏ sự hoài niệm về các phương pháp đơn giản hơn, với một người lưu ý rằng họ sẽ thích cách xử lý lỗi đơn giản của Go hơn là các hệ thống phức tạp của Rust. Tuy nhiên, những người khác lại cho rằng sự phức tạp này có mục đích - trong các ngôn ngữ có exception, việc xác định cách một hàm có thể thất bại đòi hỏi phải tin tưởng vào tài liệu hoặc đọc qua toàn bộ chuỗi gọi hàm.
So sánh các phương pháp xử lý lỗi
Phương pháp | Ưu điểm | Nhược điểm | Phù hợp nhất cho |
---|---|---|---|
anyhow | Triển khai nhanh, backtrace đầy đủ, dễ thêm ngữ cảnh | Lỗi chung chung, API ít chính xác | Ứng dụng, tạo mẫu nhanh |
thiserror | Kiểu lỗi chính xác, API ổn định, có thể so khớp các biến thể | Nhiều boilerplate hơn, hạn chế về backtrace | Thư viện, API công khai |
snafu | Lỗi dựa trên enum + backtrace, ngữ cảnh phong phú | Độ phức tạp bổ sung, đường cong học tập | Nhu cầu lai giữa thư viện/ứng dụng |
Vấn đề Backtrace không thể giải quyết
Một nguồn gây thất vọng lớn xuất phát từ việc backtrace propagation trên lỗi của Rust chưa được ổn định hóa. Hạn chế kỹ thuật này buộc các nhà phát triển phải lựa chọn giữa ergonomic tốt với toán tử ?
hoặc backtrace đúng đắn. Vấn đề phát sinh từ hệ thống trait của Rust - việc implement std::error::Error
tạo ra xung đột giữa blanket implementation cần thiết cho ergonomic và xử lý backtrace yêu cầu concrete type.
Hạn chế cơ bản này có nghĩa là các tác giả thư viện phải đưa ra những lựa chọn khó khăn mà không có giải pháp hoàn hảo. Nhóm iroh đã tìm thấy câu trả lời của họ trong snafu
, mà họ mô tả là thiserror trên steroid, cung cấp các kiểu lỗi dựa trên enum với khả năng đính kèm ngữ cảnh phong phú và tự động capture backtrace.
Sự phản đối của cộng đồng về độ phức tạp
Cuộc thảo luận tiết lộ mối lo ngại ngày càng tăng về độ phức tạp trong xử lý lỗi của Rust. Một số thành viên cộng đồng cho rằng hệ sinh thái đã làm phức tạp hóa quá mức những gì lẽ ra phải là quản lý lỗi đơn giản. Họ chỉ ra rằng các ngôn ngữ thành công như Python và Java xử lý exception một cách duyên dáng trong các dự án thực tế, đặt câu hỏi liệu phương pháp của Rust có thực sự mang lại lợi ích tương xứng hay không.
Mọi hàm đều có thể thất bại với StackOverflowError và bạn không thể làm gì về điều đó. Hầu như mọi hàm đều có thể thất bại với OutOfMemoryError và bạn không thể làm gì về điều đó. Tôi đã chấp nhận rằng mọi thứ đều có thể thất bại.
Những người khác bảo vệ structured error là thiết yếu cho các API thư viện, cho rằng việc mô hình hóa lỗi đúng đắn giảm thiểu các lỗi đối mặt với người dùng với cái giá là thời gian của nhà phát triển. Cuộc tranh luận đề cập đến việc liệu ngành công nghiệp có đang ngủ quên trên việc xử lý lỗi mà không có exception hay không, với các nhà phát triển Rust là những người đầu tiên nghiêm túc giải quyết những vấn đề này.
Khoảng trống về tài liệu và thực hành tốt nhất
Một khiếu nại lặp đi lặp lại trong cuộc thảo luận cộng đồng tập trung vào tài liệu thực hành tốt nhất rải rác của Rust. Không giống như các ngôn ngữ có hướng dẫn phong cách tập trung, các nhà phát triển Rust phải tìm kiếm qua các bài blog và thảo luận cộng đồng để tìm các khuyến nghị xử lý lỗi hiện tại. Sự phân mảnh này khiến các nhà phát triển quay lại với Rust khó biết được các phương pháp và pattern mới nhất.
Các hướng dẫn chi tiết của nhóm iroh về việc viết lỗi cụ thể - chẳng hạn như phạm vi error enum theo hàm thay vì module và bao gồm custom variant cho public trait - đại diện cho loại trí tuệ thực tế mà các nhà phát triển mong muốn được tài liệu hóa tập trung hơn.
Hướng dẫn xử lý lỗi Rust từ nhóm iroh
- Giới hạn phạm vi enum lỗi cho các hàm, không phải module - Giúp các danh mục lỗi dễ hiểu hơn
- Sử dụng tên lỗi mô tả rõ ràng -
TicketParseError
so vớiTicketError
làm rõ phạm vi của các lỗi - Bao gồm các biến thể Custom cho các trait công khai - Cho phép người triển khai truyền bá các lỗi của riêng họ
- Cung cấp các phương thức hỗ trợ -
custom_err()
vàcustom_err_box()
để tạo lỗi dễ dàng
Nhìn về phía trước: Thực dụng hơn giáo điều
Cuộc thảo luận cộng đồng gợi ý một sự nhận thức ngày càng tăng rằng các dự án khác nhau có nhu cầu xử lý lỗi khác nhau. Trong khi có áp lực đảm bảo tất cả thư viện trả về lỗi cụ thể, thực tế thực dụng là các nhóm phải cân bằng nhiều mối quan tâm bao gồm tốc độ phát triển, tính ổn định của API và khả năng debug.
Phương pháp hybrid của nhóm iroh - sử dụng structured error cho public API trong khi bảo tồn ngữ cảnh phong phú và backtrace - có thể đại diện cho một nền tảng trung gian thực tế. Tuy nhiên, những hạn chế cơ bản trong câu chuyện xử lý lỗi của Rust có nghĩa là cho đến khi backtrace propagation được ổn định hóa, các nhóm sẽ tiếp tục đưa ra những đánh đổi thay vì tìm ra giải pháp hoàn hảo.
Tham khảo: Trying to get error backtraces in rust libraries right