Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the idiomatic way to return an error from a function with no result if successful?

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.)

like image 455
Others Avatar asked Apr 27 '16 00:04

Others


People also ask

What to return if function fails?

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.


2 Answers

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>.

like image 54
WiSaGaN Avatar answered Oct 19 '22 18:10

WiSaGaN


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.

like image 42
Paolo Falabella Avatar answered Oct 19 '22 16:10

Paolo Falabella