Implementation of `try!` that works with `Option` and `Result`


#1

I know a number of people have been wanting this, but I hadn’t seen any implementations of it. In a few minutes, I threw together an implementation that I believe is backward compatible.

Comments or criticisms are welcome.


#2

so this allows using the try! macro on any implementor of the Try trait?


#4

Yes, precisely.


#5

Is .ok_or() not terse enough?


#6

The reason that ok_or is not enough is that the value that try!() adds is the return on non-happy path behavior. Sometimes you just really want to bubble up the sad case without worrying about it in your method chaining.


#7

Using .ok_or with try! returns a Result from the function. The idea with this implementation is that try!(some_option) would return None from the function when some_option is None.


#8

I think that Try::try should use its own struct for the return type.

enum FlowPath<H, B> {
  Happy(H),
  Bubble(B)
}

With whatever bikeshedding you want for the names. (Probably do research into what Programming Language Theory calls these things?)

And then, you’d have Try::try(self) -> FlowPath<H, B>.

Basically, don’t overload Result into Either and instead create your own structurally equivalent type. It’d make the code semantically more readable.

Actually, I’m going to fork and show what it’d look like.

Edit: Done here: https://github.com/Havvy/try/tree/patch-1


#9

Oh okay, cool. Neat. I thought it just elevated the Option to a Result. Sorry.


#10

Looks really useful, any plans to make this an RFC?


#11

Yeah, I’ll start writing up an RFC now.


#12

I’m not sure if this really has a strong motivation. “It would be neat.” is not very compelling. I only found about 30 cases of None => return None in all the Rust compiler/stdlib code.

Does anyone have any ideas for other types that might benefit from implementing Try?


#13

FWIW I have a similar trait (called Carrier) in the trait-based exception handling RFC.


#14

Here is another attempt at this :slight_smile: https://github.com/SimonSapin/rust-std-candidates/blob/master/triable/lib.rs


#15

Well, dang. I tried compiling my implementation in std and it fails with a type inference error:

src/libstd/try.rs:77:21: 77:36 error: type annotations required: cannot resolve `_ : try::Try<collections::vec::Vec<net::addr::SocketAddr>, core::result::Result<collections::vec::IntoIter<net::addr::SocketAddr>, io::error::Error>>` [E0283]
src/libstd/try.rs:77         match $crate::try::Try::try($e) {
                                         ^~~~~~~~~~~~~~~
src/libstd/try.rs:75:1: 82:2 note: in expansion of try!
src/libstd/net/addr.rs:385:21: 397:18 note: expansion site
src/libstd/try.rs:77:21: 77:36 note: required by `try::Try::try`
src/libstd/try.rs:77         match $crate::try::Try::try($e) {
                                         ^~~~~~~~~~~~~~~
src/libstd/try.rs:75:1: 82:2 note: in expansion of try!
src/libstd/net/addr.rs:385:21: 397:18 note: expansion site
error: aborting due to previous error

So, it’s not backward compatible. Game over.


#16

I have a pretty good use case for this. I’m writing a chat server which should ideally never crash, so I’m doing the usual check all options and results and “handle” them. But in a lot of cases the unwraps are really just protecting me from things which I cannot reasonably recover from, so rather than crash I’d like to have some unified logic for logging these events. The recovery logic should be applicable to both Option<T> and Result<T, E>.

More fundametally it’s obvious Option and Result need unification as their functions overlap a lot.