In Rust, I believe the idiomatic way to deal with recoverable errors is to use Result. For example this function clearly is idiomatic:
fn do_work() -> Result<u64, WorkError> {...}
Of course, there are also functions that have a single, obvious, failure state, and therefore use the Option type instead. An idiomatic example would be this:
fn do_work() -> Option<u64>
This all is straightforwardly addressed in the documentation. However, I'm confused about the case where a function can fail, but has no meaningful value when successful. Compare the following two functions:
fn do_work() -> Option<WorkError> // vs fn do_work() -> Result<(), WorkError>
I'm just not sure which one of these is more idiomatic, or is used more often in real world Rust code. My go-to resource for questions like this is the Rust book, but I don't think this is addressed in its "Error Handling" section. I haven't had much luck with any other Rust documentation either.
Of course this seems pretty subjective, but I'm looking for authoritative sources that either state which form is idiomatic, or on why one form is superior (or inferior) to the other. (I'm also curious how the convention compares to other languages that heavily utilize "errors as values", like Go and Haskell.)
If you make the function to do some operation , return 0 for success , -1 for failed, and set errno to appropriate value so that the caller could check it to know the detail of failure. Sometimes people also use different return values to mean different failures.
Use fn do_work() -> Result<(), WorkError>
.
Result<(), WorkError>
means you want the work to be done, but it may fail.
Option<WorkError>
means you want to get an error, but it may be absent.
You probably want the work to be done but not to get an error when you write do_work()
, so Result<(), WorkError>
is the better choice.
I would expect Option<WorkError>
only be used in cases like fn get_last_work_error() -> Option<WorkError>
.
Rust is "pretty strongly typed" (and please, please don't call me out on how I measure how strongly typed a language is...). I mean this in the sense that Rust generally gives you the tools to let types "speak" for you and document your code, therefore it's idiomatic to use this feature to write readable code.
In other words, the question you're asking should be more "which type represents best what the function does to anyone who reads its signature?"
For Result<(), Workerror>
you can see straight from the docs
Result is a type that represents either success (Ok) or failure (Err)
So, specialized for your case, it means your function returns nothing if it's successful (represented by Ok<()>
) or WorkError
if there's an error (Err<WorkError>
). This is a very direct representation in code of the way you described the function in your question.
Compare this to Option<WorkError>
or Option<()>
Type Option represents an optional value: every Option is either Some and contains a value or None, and does not
In your case Option<WorkError>
would be saying to the reader "this function should return a WorkError
but it may return nothing". You can document that the "return nothing" case means that the function was actually successful, but that's not very evident from types alone.
Option<()>
says "this function can return nothing or have no meaningful return", which can be a reasonable thing to say if WorkError
contains no other info (like an error type or an error message) and it's practically only a way to say "an error has occurred". In this case a simple bool
carries the same information... Otherwise the Result
lets you return some more info associated with the error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With