Is there something more idiomatic than Option<Result<T, string>>?

Hello, I'm working on a parser and I'm scanning the input string using a (peekable) iterator.

I have a function that consumes items from the iterator and should return three different things:

  • a token if it manages to parse one
  • an error message when something goes wrong
  • something that tells me that I reached the end of the iteration on the input string.

I have a working example using Option<Result<Token, string>>. It works fine but it doesn't feel very idiomatic.

What do you think about it? I'd like to know if there is some enum that already combine option and result or if I should roll my own since it would be very easy.

I think Option<Result<T, E>> is quite idiomatic. Not only that, but it seems like a good fit, since it exactly matches the case analysis that you've listed in the contract of your parser. This type signature is used in std too. For example, the BufReader::lines returns a Lines iterator whose next method returns a Option<Result<String, io::Error>>.

My one caveat here is that you probably shouldn't use String for your error type. Instead, you should create your own Error type and define each of its variants to correspond to a possible error case. Here's an extreme example for a regex parser, but you could use a larger granularity if you wanted.

5 Likes

From a glance, the only small thing I would do different is make it Result<Option> instead. To me this says "I tried to parse it, it may have failed. If the parsing worked there may or may not actually be a value there" where Option<Result> says "I may or may not have tried to parse it. If it did try it may have failed". This might be what you are trying to express though

6 Likes

I agree, Result<Option> is generally more idiomatic. It also has the added benefit of allowing you to try! / ? the Result out of the way first before deciding what to do with the Option.

...Although, in the case of an Iterator, I might go the other direction, as iterators build the result inside rather than around.

4 Likes

After having tried both the approaches out I confirm that with an iterator Option<Result<T,E>> is more natural.

2 Likes