-
Notifications
You must be signed in to change notification settings - Fork 20
Error handling in proot‐rs
Unlike other languages, Rust's exception handling seems very clean and efficient.One of the advantages is that a new kind of Error can be easily defined. proot-rs defines an Error as follows, and the relevant code is in proot-rs/src/errors.rs
.
pub struct Error {
errno: Errno,
msg: Option<Box<dyn Display + Send + Sync + 'static>>,
source: Option<Box<dyn std::error::Error>>,
}
We deliberately added the errno
field because most of proot-rs is related to system call translation, and in many places it is necessary to return errno
to tracee. the msg
field is used to help the developer locate the reason why the error occurred, and source
points to another error that caused this error to occur, just like a chain.
One might ask, why not use thiserror
, anyhow
, or a design like std::io::Error
?
First, thiserror
and std::io::Error
are similar in approach, except that the former is a bit more convenient. In fact, proot-rs once does just that: it defines Error
directly as an large enumeration, just a wrapper for other types of errors.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Error {
Sys(Errno, &'static str),
InvalidPath(&'static str),
InvalidUtf8,
IOError(IOErrorKind),
UnsupportedOperation(&'static str),
}
There are some problems with this design.
- In some scenarios, we need to set the value of errno manually, which is obviously not very convenient to do with this design. (How can we set a different value of
errno
forError::IOError(IOErrorKind)
?) - Difficult to extend, whenever we encounter a new exception (like using a new crate or something like that), we have to add an item to this enumeration
- Chained Exceptions are not supported, which makes the root cause of the error missing.
anyhow
can solve the last two problems, but not the first one. anyhow
is nice and uses some unsafe
ways to improve efficiency, which makes it hard to extend it. In fact, we tried to extend anyhow
to record errno
, but we failed.
In the end we chose to implement a struct Error
ourselves. We tried to achieve a development experience as close to anyhow
as possible. This of course includes Result<T>::context()
, Result<T>::with_context()
, and our own addition of Result<T>::errno()
.
If you are a supporter of anyhow
, you should not feel any discomfort in proot-rs. Just add use crate::errors::*;
to the front of the source file, and then as always. :)