So this is where the fundamental disagreement lies, I think. It seems (if I'm understanding you correctly) that you're wanting to eliminate the bump at the point (which I totally agree exists) by encoding roughly what they think is happening syntactically – they can basically just keep moving until at some later point they need to understand what's going on "under the hood" so to speak (where the hood is the syntactical sugar for the types). In that regard, your analogy to for
makes sense, and it's certainly something we do regularly in the language.
I differ because I think it's actively helpful to hit that bump, normally relatively early, and then deeply internalize that errors are just like everything else – and accordingly have all the same machinery and tools (and a few extra niceties, as well!) as everything else. They're not a special case. I don't want to coat over that with sugar, because I think that bump is pedagogically useful! That difference has been an essential part of how I help people understand how Rust solves problems differently from (and, in many ways, better than) what they're used to.
(The existing solution (?
) has the advantage of being something that clearly layers on top of those base semantics, rather than being an entirely alternative way of expressing the same concept – especially as regards return type. It analogizes clearly, and accurately, to people's experience with short-circuiting operators in other languages, whether the Elvis operator in C♯ or even further back to the old-fashioned ternary.)
Moreover, I think that syntactically encoding something that looks like what they expect from Python, Java, C♯, etc, but which has profoundly different semantics seems very, very likely to mislead – to get users thinking that errors map to a concept they already know, when in fact they map to something very different. Then you have unlearning to do.
In particular:
In my view, this is an intuitive understanding we should actively discourage, as people's assuming it will fundamentally mislead them. Error-handling in Rust is not like exception-based handling,[1] and we shouldn't encourage the idea that they're similar, even for the sake of smoothing the on-boarding process.
I also think there are really important differences between your example of for
loops and Iterator
and this proposal.
for
loops in other languages are are also often implemented in terms of underlying iterator machinery. Similarly, the for
loop in Rust, despite being implemented in terms of Iterator
, has very much the same semantics as a for
loop in C++ or Java or C♯ or Python. By contrast, the proposed mechanism here has no analogy I'm aware of in another language: the closest is probably Java's checked exceptions, but those certainly aren't return types, and the similarity between those and this proposal is syntactical and superficial. And unlike for
, the semantics of throw
as proposed here are totally unlike the semantics of throw
in other languages – they have radically different implications for both control flow and what the actual value returned will be.
I want to be clear (tone in text being difficult) that despite my strong disagreement about this specific proposal, I'm with you on the ratcheting as a strategy in general, and I think we have very similar goals in the main; this may simply be a matter of preference in pedagogical strategy.
And that brings me to my closing question: are we trying to solve an ergonomics problem, a pedagogical problem, or both? To reiterate @nikomatsakis' comment, I think it's really important to be clear on exactly what the motivation is to get the solution right.
Note: I'll be watching this and may chime in briefly, but I've already spent more time than I could afford on this, so please understand that it's not personal to anyone if I don't respond in here again!
Footnotes
- An addendum from my own experience (granting fully that I'm a sample size of one, and I have… my own peculiarities … so I won't lean too much on this; thus sticking it in a footnote). The idea that
Result
is just another type was one of the things that made me fall in love with Rust. It's literally something I had wanted for half a decade or more. I was sick of dealing with exception-based error handling, and had built my own terrible enum- and union-based solutions trying to work around it at times in the past in C; I'd experimented in Python and JavaScript with my own ad hoc class-based, non-exception-using solutions as well. I don't want other people not to get to have the joy of "OH IT'S JUST A VALUE!!!" that I got.