Error handling can be tedious and extermely annoying... On most programming languages, but using Rust it's actually very simple !
Of course coding methods are a matter of preference and I am nobody to say this is better than something else, but here's my 2 cents on this subject, and how I prefer to do it. What I usually do with my projects is that I setup a simple error handling type In the whole project, I write any function that can result in an error with a return type of When I perform a serialization using Here comes the With this setup, I can use the code abusing the If you look at the code block defining all the I repeated my code more than 2 times, it's worth spending an hour writing an automation macro ! Luckily for you, I did it for you ;-) There are 3 parts in the process we want to automate: We will use the pattern Inside our macro "code", we will use some So something like: Would generate something like: This is now what our The good thing with this macro is that at any particular part of your project you can create a new error type, and will just have to add it to the list ! Then you can have things like: See how in the How I organise error handling in my projects
pub enum Errcode
in a file src/errors.rs
, and I define every possible kind of errors in it.Result<_, Errcode>
, allowing me to use the ?
operator everywhere, returning the error till it reaches the very main
function of my binary.Error conversion
serde
, or some function returning a std::io::Error
, How can I transform the error to my "standardized" one ?From
trait:
?
operator:use crate Errcode;
Avoiding code repetition
impl From<_> for Errcode
below, you can see how this can be annoying after a while.From<_> for Errcode
trait on the enumGetting the arguments of the macro
[ $( $name:ident : $class:ty ),+ ]
, meaning that the macro define_errcodes
will have the formdefine_errcodes![ NAME_A : CLASS_A, NAME_B : CLASS_B, ... ]
[ .. ]
defines the characters before and after the arguments list$( ... ),+
defines the repetition, saying that args are delimited by ,
and that there is at least one argument.( A : B )
means that the two arguments in one repetition are separated by a :
(it cannot be a ,
as it would be confusing)$name:ident
means $name
is an Identifier, same as a variable / function name$class:ty
means $class
is a Type, it will be checked during the compilationGenerating code for each argument
$( <code> )+
blocks, which will loop through all our arguments and generate the <code>
for each of them.println!;
$
println!;
println!;
println!;
println!;
// ...
Our final macro code
src/errors.rs
file look:define_errcodes!;
Scoped yet global error handling
// src/errors.rs
define_errcodes!;
// src/server.rs
}
// src/main.rs
init_server
everything is supposed to return a Errcode
error case but in our try_bind_port
we return a ServerError
? The ?
operator will perform the conversion itself, and that allows to write clean and readable code, without all the error-handling-related code.