How to deal with nested results & options?


#1

Hi! In rust-analyzer, I have a bunch of code which returns Cancelable<Option<T>>, where Cancelable<T> is Result<T, Canceled>. Here’s an example: https://github.com/rust-analyzer/rust-analyzer/blob/d1b993c0aacb2a29fc87283a8b35fe1b4a542459/crates/ra_analysis/src/imp.rs#L281-L285

The Cancelable means that the operation can be canceled at any time, and Option is the usual “no reasonable value here”.

Such signatures are a pain to work with, because ? does not work with nested options. It is especially visible when you convert an Option returning function into a Cancelable<Option> returning function: all those sweet ? have to be converted to the ctry! macro.

What are the practical approaches to deal with this problem in stable Rust (1.31.0 would also work)? Is the xtry! macro the best we can do?

Ideally, I’d love to have some .lift extension function on optionals which allows me to apply ?, but that seems impossible: ? can return only the Err value, and I need to return an Ok(None).

An additional property of my situation is that I inspect (“handle”) None values pretty regularly, but I never inspect the Err(Canceled) value, I always propagate it upwards.


#2

You could use the transpose function to swap the Option an Result, but thats currently on nightly, if you want this on stable you could create a small extension trait to do it.

https://doc.rust-lang.org/std/result/enum.Result.html#method.transpose

https://doc.rust-lang.org/std/option/enum.Option.html#method.transpose

Then you could use the ? operator like normal.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=96ab6b43f5ea7180ab5d32300d77a05c


#3

In addition to the unstable method transpose() and the possibility to write this yourself, there are also the crates result and insideout providing this functionality.


#4

I don’t think transpose can help me in this situation? Here’s the minimal playground which shows what I am trying to achieve:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=037fce9ec843f79c433ffc6d3dc0a8b0


#5

Ah, I see. Well, the best I could come up with involves unstable try blocks and separating functions based on whether they return Cancelable<Option<T>> or plain Option<T>: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=cc487e4ffd46c57a4e07d62709eee914

Would it maybe make sense to upgrade Cancelable<Option<T>> into a proper type with its own impl of the Try trait?


#6

I have some (majority, actually) cases where I use just Cacelable<T>.

Well, the best I could come up with involves unstable try blocks

Hm, that seems interesting! I wonder if we can extend try blocks to say “this ? breaks out from the outer try”, sort of how labled returns work.


#7

I remember a suggestion for labeled try operators (link), but I wouldn’t hold my breath waiting for them.

I’ve come up with an even shorter version that combines try and transpose(), but it’s unfortunately less scalable: Playground link. As you can see, it requires a type annotation and explicitly mapping over the Cancelable. But I thought it might still be interesting.