Errors in Rust
In Rust, error handling is typically done using the Result
type, which represents either success (Ok
) or failure (Err
). This approach is similar to Go’s explicit error return, but with added type safety and pattern matching capabilities.
use std::fmt;
use std::error::Error;
// By convention, we define our own error types to represent specific error conditions
#[derive(Debug)]
struct TeaError {
message: String,
}
impl fmt::Display for TeaError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl Error for TeaError {}
// We define our error constants as static strings
static ERR_OUT_OF_TEA: &str = "no more tea available";
static ERR_POWER: &str = "can't boil water";
fn f(arg: i32) -> Result<i32, Box<dyn Error>> {
if arg == 42 {
// We use the ? operator to return errors early
Err(TeaError { message: "can't work with 42".to_string() }.into())
} else {
Ok(arg + 3)
}
}
fn make_tea(arg: i32) -> Result<(), Box<dyn Error>> {
match arg {
2 => Err(TeaError { message: ERR_OUT_OF_TEA.to_string() }.into()),
4 => {
// We can wrap errors to add context
Err(TeaError { message: format!("making tea: {}", ERR_POWER) }.into())
},
_ => Ok(()),
}
}
fn main() {
for i in &[7, 42] {
// We use pattern matching to handle the Result
match f(*i) {
Ok(r) => println!("f worked: {}", r),
Err(e) => println!("f failed: {}", e),
}
}
for i in 0..5 {
match make_tea(i) {
Ok(_) => println!("Tea is ready!"),
Err(e) => {
// We can match on the error message to identify specific errors
match e.to_string().as_str() {
ERR_OUT_OF_TEA => println!("We should buy new tea!"),
s if s.contains(ERR_POWER) => println!("Now it is dark."),
_ => println!("unknown error: {}", e),
}
}
}
}
}
In this Rust code:
We define a custom
TeaError
type that implements theError
trait.Instead of using sentinel errors, we use static strings to represent specific error messages.
Functions that can fail return a
Result
type, which is eitherOk
with a value orErr
with an error.We use
Box<dyn Error>
to allow returning different error types from our functions.The
?
operator is used for early returns of errors, similar to how Go often checks for errors immediately.In
main
, we use pattern matching (match
) to handle theResult
types returned by our functions.To check for specific errors, we match on the error message string. This is less type-safe than Go’s
errors.Is
, but it’s a common pattern in Rust when dealing with error messages.
This Rust code demonstrates error handling patterns that are idiomatic to Rust while maintaining a similar structure to the original Go code.