This might seem like an very broad, fundamental and conservative reply, but I’m not convinced. (FWIW, I’m also not convinced by catch, less so by “Ok-Wrapping”)
First of all, to get it out of the way: I don’t like throw and catch, as words, as they point towards exception handling. This is amendable, though, better words can be found.
My issue is that error handling in Rust currently has a nice property that’s rarely appreciated: It works the same in any context. Just as Ok-wrapping does, this proposal puts emphasis on the case where Result is a return type. I’d like to make a case that while this case seems so dominant it merits its own ergonomics surface, it really isn’t.
Results are central to Rust, but Result flow really isn’t. I’d like to give two cases are currently very intuitive and useful in the current language, which with these proposal both don’t get better and suddenly become cases of “Rust cares about this, but not about that”.
The first one being types which aren’t Results, but have a direct result to Result. The dominant example here is Future:
trait Future {
type Item;
type Error;
fn wait() -> Result<Self::Item, Self::Error>;
}
pub trait IntoFuture {
type Future: Future<Item = Self::Item, Error = Self::Error>;
type Item;
type Error;
fn into_future(self) -> Self::Future;
}
A Future can kind of be seen as a future Result and a Result as an immediate Future (and indeed, the Future API is very much built around that idea).
Let’s have a look at a function returning a leaf:
fn leaf(success: bool) -> FutureResult<String, String> {
let res = if success {
Ok("yeah!".into())
} else {
Err("ouch".into())
};
res.into_future()
}
(This example is a little more verbose then necessary, the ok and err functions of futures would cut that down much more)
To my understanding of the RFC, this would stay as is, as FutureResult is fundamentally a different type then Result and catch cannot express that. This would lead to the odd case where the work of making similar concepts expressible in a similar fashion would suddenly be broken. Current Rust, by making Results not all too special, very nicely maintains that you can easily use your own type without losing ergonomics.
Another case is error flow that is not stack based. This is regularly the case when talking about systems that have no useful notion of a stack from a systems view such as actors, streams and channels.
Let’s take a simple example from futures here, again:
fn main() {
let (sender, receiver) = oneshot::channel::<Result<String, String>>();
sender.send(Ok("foo".into()));
}
Again, even with all these proposals, I’m back to explicitly constructing errors. Neither this proposal nor other Ok-wrapping proposals help here.
Finally, making catch a keyword only possible on functions strikes me as odd here, to my knowledge, it would be the only keyword that can express a type on a function in a way that isn’t possible anywhere else.
The beauty about the current error/result story of Rust is that all the above cases are similar to handling returned results (with the small exception of ?, which I consider a modest addition).
I see a lot of reference about “introducing a way to speak about errors” into the language: we already have that. Results are values, with all the freedom that this gives you, especially them not expressing anything about flow. In my opinion, this is a feature, not a bug.
Much as with other, similar proposals, I would also appreciate extensive user research into concrete problems that the current system has. In my experience teaching Rust, the current system is easy to teach and understandable. It is verbose, but this a thing that the language has never shied away from.
My gut feeling is that this proposal will lead to a situation similar to do in Haskell: it’s there, it’s convenient, but when you get serious, you throw the sugar away and go back to do it all by hand.