Error Handling
Common Error Handling Approaches in Rust Applications
Error handling in Rust differs from languages like Java; it lacks constructs like try...catch. The typical approach is to define a global error handling type at the application level:
Here, the thiserror library is used, which facilitates defining custom error types and simplifies code. For brevity, an AppResult type alias is also defined.
thiserror vs. anyhow
In the Rust error handling ecosystem, two commonly used libraries are thiserror and anyhow:
-
thiserror: Suitable for library developers to define clear error types. It uses derive macros to help implement the
std::error::Errortrait for custom error types while allowing you to define error representations. When building a library or providing clear error types to users,thiserroris the better choice. -
anyhow: Geared toward application developers, it provides a generic error type
anyhow::Errorthat can encapsulate any error implementing thestd::error::Errortrait. It focuses more on error propagation than definition, making it particularly suitable for application-layer code. You can quickly convert various errors intoanyhow::Error, reducing the need for boilerplate code.
In some scenarios, you might use both libraries: define error types with thiserror in libraries and handle and propagate these errors with anyhow in applications.
Error Handling in Handlers
In Salvo, Handlers often encounter various errors, such as database connection errors, file access errors, network connection errors, etc. For these types of errors, the aforementioned error handling approach can be applied:
Here, home directly returns an AppResult<()>. But how should this error be displayed? We need to implement the Writer trait for the custom error type AppResult, where we can decide how to display the error:
In Salvo, a Handler can return a Result, provided that both the Ok and Err types in the Result implement the Writer trait.
Error Handling with anyhow
Given the widespread use of anyhow, Salvo provides built-in support for anyhow::Error. When the anyhow feature is enabled, anyhow::Error implements the Writer trait and is mapped to InternalServerError:
To use the anyhow feature, enable Salvo's anyhow feature in Cargo.toml:
This allows your handler functions to directly return anyhow::Result<T>:
Errors often contain sensitive information, which generally shouldn't be visible to regular users for security and privacy reasons. However, if you're a developer or site administrator, you might prefer errors to be fully exposed, revealing the most accurate error information.
As shown, in the write method, we can access references to Request and Depot, making it convenient to implement the above approach:
Displaying Error Pages
Salvo's built-in error pages meet requirements in most cases, displaying Html, Json, or Xml pages based on the request's data type. However, there are situations where custom error page displays are still desired.
This can be achieved by implementing a custom Catcher. For detailed instructions, refer to the Catcher section.