Return Result<T> or Option<T>?


#1

Which is better and why?

fn from_str(s: &str) -> Option {
fn from_str(s: &str) -> Result<MyType, MyError> {

I am converting some user(external) input into my type, with validation.


#2

The Result returning one but only if callers care about handling the error. If they don’t, then it doesn’t really matter. This is why Result::ok() exists - any caller can turn it into an Option to “drop” the error.


#3

The semantics of returning Result vs. Option are like this, in my head at least:

  • Option<T> - The function may or may not return a value, but both scenarios are valid (i.e. neither is an ‘error’).
  • Result<T, E> - The function may return a value, or it might fail.
  • Result<Option<T>, E> and Option<Result<T, E>> - The function may fail; if it doesn’t, there’s valid scenarios where nothing will be returned.

In your case, I’d probably go for one of the latter two, to make it clear to the consumer that there’s scenarios in which the operation might not be successful.


#4

Can the consumer of the API do intended work in case error/none?

Yes -> use option
No -> use result. In this case failed validation should prohibit consumer from doing what’s intended (create user or whatever it is). Also you want indicate why exactly validation failed, so you need error.


#5

Several responses of “it depends” with considerations about the needs of the consumer, and reporting the nature of the input error, all good.
One thing that might be useful to think about is the layering between these considerations. You don’t say how the input is provided and where errors might be reported (e.g. interactively, config file, database, API calls, …) and this might matter. But considering the most general and layered approach:

  • you might be using some underlying basic parser that will return one of these types, usually Result with some error detail
  • you might have some basic syntax validation, and want to report errors
  • you might have some more detailed validation of content rules, referential integrity or ownership/permissions, etc. You want to report meaningful/helpful errors on the rules being validated.
  • at some point you have a fully-validated object, or you don’t. You’ve reported why and done all that’s feasible to correct or recover. Unless this was a fatal error, you want to continue.
  • you have the rest of your program, that just wants to consume fully-validated objects. Some of them might not exist at all, and some of the things that failed to validate you might have decided to treat as though they didn’t exist.

So, as general guidance as you move up through these layers, you want:

  • Result, while still handling potential errors or reporting validation failures (perhaps even several different ones with different Error types)
  • Option, where it’s perfectly normal for a thing not to exist (it’s optional)
  • Option probably via Result::ok(), marking the point where you’ve made the explicit decision to treat an invalid input as a non-existent object.
  • Your bare object, when it’s mandatory and has been validated.

#6

Mind that there already is a FromStr trait that provides a from_str() method returning Result. That means if you implement it, you’ll get the following syntax for free:

let value: MyType = the_string.parse()?;