Short version: I’m also leaning towards try as the keyword for ? scoping. The behavior of these ? blocks is much closer to how existing languages use try than it is to how they use catch, and I feel like the word “try” is much less tied to any specific notion of exceptions than “throw” and “catch” are.
Now for the much longer version, where I shall begin by bikeshedding what you think the bikeshed even is:
This is actually very far off what I thought the “standard” perspective/terminology was for all of these things, outside the realm of research languages and academia and programming language enthusiasts, so maybe it’ll help if I spell out the terminology and narrative that I’m used to from my C++/Javascript-based corner of the not-yet-Rust-using industry.
In my experience “error handling” is the generic term for all the stuff we’re talking about, and the hardest part of it is propagating errors when the error simply cannot be handled right when it occurs. The two major approaches are “return codes” that are explicitly, manually propagated, and “exceptions” which are implicitly, automatically propagated. Hence the metaphor of throwing and catching; once thrown an exception does not stop flying up the stack until it hits something that’s ready and waiting to catch it. Return codes stay right where they are unless code is written at each layer to propagate them. And this leads into the usual narrative of why exceptions are a big improvement over return codes (at least the way they turned out in C): exceptions are a lot harder to ignore on accident, and failing fast is better than delaying the inevitable.
With Rust, everyone’s still forming their own narrative, but mine is that Rust made pervasive, explicit return code propagation feasible by reducing it to literally one single character. Manual error handling was always theoretically better, since there’s no longer this invisible extra codepath that behaves very differently to all other code in the language. And now that the ergonomics issue is solved, it’s also better in practice. In particular, that theoretical benefit of more uniform semantics helps when you want to do any non-trivial error propagation, such as bundling up all the errors that happened in various worker threads in a way that the main thread can figure out how to report them properly. Automatically propagated errors make that very hard to do correctly. When typical, trivial propagation is ergonomic enough to do manually all the time, that makes it straightforward to do non-trivial things when you need to, because the same language rules apply to error values as any other kind of value. This is a lot like how in Javascript the async/await keywords make the typical, trivial control flow for async code very ergonomic, but you can always fall back to manipulating promises if you need some non-trivial async control flow like a Promise.all or Promise.race or whatever.
I agree with all of this in principle. Some keywords and jargon have a lot of intrinsic “wiggle room”, vagueness, or variance among existing languages, such that it’s a net benefit to reuse them for a similar-but-not-identical feature in our shiny new language. Such as “try”. But that doesn’t mean there aren’t keywords and jargon out there with a specific enough established meaning that it would do more harm than good to apply it to something fundamentally different.
I dislike the use of catch specifically because I believe that most mainstream programming languages use “throw”, “catch” and “exception” to refer unambiguously to an error handling mechanism where propagation is implicit, as opposed to the manual propagation of return codes. This is certainly not true of all programming languages ever made, and there’s plenty of variation in how exceptions have been implemented even among these mainstream languages, but I do think the typical working industry programmer discovering Rust is more likely to associate “throw”, “catch” and “exception” with implicit propagation mechanisms in particular than with error handling in general. So I still think that using those terms for Rust would be a net loss (unless we apply them to panics, of course, which catch_panic already does).
But I do not feel that way about “try”. I’m not entirely sure how to justify this (though it’s probably impossible to convincingly justify any claim of this sort anyway), but I think “try” has a lot more wiggle room than “catch” does, and it would be perfectly fine to apply “try” to Rust’s ?-scoping blocks. I suspect this is because in mainstream languages, the “try” construct “doesn’t do anything”; it’s merely setup for a later keyword that does the real work. In a try/catch construct, it’s the catch that actually does something. In a try/finally, it’s the finally that actually does something. With Javascript promises, there is no syntactic need for a “try” keyword, so it disappears completely; the interesting work is all done by methods like .catch() and .finally(), and there simply is no .try().
Maybe I’m wildly biased and none of this rambling applies to “the programming community” as a whole, but that’s where I was coming from when I said I didn’t like the use of catch for this feature.