mirror of https://github.com/rust-lang/rfcs.git
917 lines
38 KiB
Markdown
917 lines
38 KiB
Markdown
- Feature Name: `try_expr`
|
||
- Start Date: 2018-04-04
|
||
- RFC PR: [rust-lang/rfcs#2388](https://github.com/rust-lang/rfcs/pull/2388)
|
||
- Rust Issue: [rust-lang/rust#50412](https://github.com/rust-lang/rust/issues/50412)
|
||
|
||
# Summary
|
||
[summary]: #summary
|
||
|
||
[RFC 243]: https://github.com/rust-lang/rfcs/blob/master/text/0243-trait-based-exception-handling.md#choice-of-keywords
|
||
|
||
[RFC 243] left the choice of keyword for `catch { .. }` expressions unresolved.
|
||
This RFC settles the choice of keyword. Namely, it:
|
||
|
||
1. reserves `try` as a keyword in edition 2018.
|
||
2. replaces `do catch { .. }` with `try { .. }`
|
||
3. does **not** reserve `catch` as a keyword.
|
||
|
||
# Motivation
|
||
[motivation]: #motivation
|
||
|
||
[catch_rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0243-trait-based-exception-handling.md
|
||
|
||
[catch_rfc_motivation]: https://github.com/rust-lang/rfcs/blob/master/text/0243-trait-based-exception-handling.md#catch-expressions
|
||
|
||
This RFC does not motivate `catch { .. }` or `try { .. }` expressions.
|
||
To read the motivation for that, please consult [the original `catch` RFC][catch_rfc_motivation].
|
||
|
||
## For reserving a keyword
|
||
|
||
Whatever keyword is chosen, it can't be contextual.
|
||
|
||
As with `catch { .. }`, the syntactic form `<word> { .. }` where `<word>`
|
||
is replaced with any possible keyword would conflict with a struct named
|
||
`<word>` as seen in this perfectly legal snippet in Rust 2015,
|
||
where `<word>` has been substituted for `try`:
|
||
|
||
```rust
|
||
struct try;
|
||
fn main() {
|
||
try {
|
||
};
|
||
}
|
||
```
|
||
|
||
### Aside note:
|
||
|
||
The snippet above emits the following warning:
|
||
|
||
```
|
||
warning: type `try` should have a camel case name such as `Try`
|
||
```
|
||
|
||
which is also the case for `catch`.
|
||
This warning decreases the risk that someone has defined a type named `try`
|
||
anywhere in the ecosystem which happens to be beneficial to us.
|
||
|
||
## For reserving `try` specifically
|
||
|
||
This is discussed in the [rationale for `try`][rationale for try].
|
||
|
||
# Guide-level explanation
|
||
[guide-level-explanation]: #guide-level-explanation
|
||
|
||
The keyword `try` will be reserved.
|
||
This will allow you to write expressions such as:
|
||
|
||
```rust
|
||
try {
|
||
let x = foo?;
|
||
let y = bar?;
|
||
// Note: OK-wrapping is assumed here, but it is not the goal of this RFC
|
||
// to decide either in favor or against OK-wrapping.
|
||
x + y
|
||
}
|
||
```
|
||
|
||
# Reference-level explanation
|
||
[reference-level-explanation]: #reference-level-explanation
|
||
|
||
[list of keywords]: https://doc.rust-lang.org/book/second-edition/appendix-01-keywords.html#keywords-currently-in-use
|
||
|
||
The word `try` is reserved as a keyword in the [list of keywords]
|
||
in Rust edition 2018 and later editions.
|
||
|
||
The keyword `try` is used in "try expressions" of the form `try { .. }`.
|
||
|
||
# Drawbacks
|
||
[drawbacks]: #drawbacks
|
||
|
||
There are two main drawbacks to the `try` keyword.
|
||
|
||
## Association with exception handling - Both a pro and con
|
||
|
||
> I think that there is a belief – one that I have shared from time to time – that it is not helpful to use familiar keywords unless the semantics are a perfect match, the concern being that they will setup an intuition that will lead people astray. I think that is a danger, but it works both ways: those intuitions also help people to understand, particularly in the early days. So it’s a question of “how far along will you get before the differences start to matter” and “how damaging is it if you misunderstand for a while”.
|
||
>
|
||
> [..]
|
||
>
|
||
> Rust has a lot of concepts to learn. If we are going to succeed, it’s essential that people can learn them a bit at a time, and that we not throw everything at you at once. I think we should always be on the lookout for places where we can build on intuitions from other languages; it doesn’t have to be a 100% match to be useful.
|
||
|
||
\- [Niko Matsakis](https://internals.rust-lang.org/t/bikeshed-rename-catch-blocks-to-fallible-blocks/7121/4)
|
||
|
||
For some people, the association to `try { .. } catch { .. }` in languages such
|
||
as Java, and others in the [prior-art] section, is unhelpful wrt. teachability
|
||
because they see the explicit, reified, and manually propagated exceptions in
|
||
Rust as something very different than the much more implicit exception handling
|
||
stories in Java et al.
|
||
|
||
[`ExceptT`]: https://hackage.haskell.org/package/mtl-2.2.2/docs/Control-Monad-Except.html#t:ExceptT
|
||
|
||
However, we make the case that other languages which do have these explicit and
|
||
reified exceptions as in Rust also use an exception vocabulary.
|
||
Notably, Haskell calls the monad-transformer for adding exceptions [`ExceptT`].
|
||
|
||
We also argue that even tho we are propagating exceptions manually,
|
||
we are following tradition in that other languages have very different
|
||
formulations of the exception idea.
|
||
|
||
The benefit of familiarity, even if not a perfect match, as Niko puts it,
|
||
helps in learning, particularly because Rust is not a language in lack of
|
||
concepts to learn.
|
||
|
||
[`try!`]: https://doc.rust-lang.org/nightly/std/macro.try.html
|
||
|
||
## Breakage of the [`try!`] macro
|
||
|
||
One possible result of introducing `try` as a keyword be that the old `try!`
|
||
macro would break. This could potentially be avoided but with great technical
|
||
challenges.
|
||
|
||
With the prospect of breaking [`try!`], a few notes are in order:
|
||
|
||
1. `?` was stabilized in 1.13, November 2016, which is roughly 1.4 years since
|
||
the date this RFC was started.
|
||
2. `try!` has been "deprecated" since then since:
|
||
> The `?` operator was added to replace `try!` and should be used instead.
|
||
3. `try!(expr)` can in virtually all instances be automatically `rustfix`ed
|
||
automatically to `expr?`.
|
||
4. There are very few questions on Stack Overflow that mention `try!`.
|
||
5. ["The Rust Programming Language", 2nd edition](https://doc.rust-lang.org/book/second-edition/) (book) and "Rust by Example"
|
||
have both already removed all mentions of `try!`.
|
||
|
||
> So overall I think it’s feasible to reduce the `try!` macro to a historical curiosity to the point it won’t be actively confusing to newbies coming to Rust.
|
||
|
||
\- [kornel](https://internals.rust-lang.org/t/bikeshed-rename-catch-blocks-to-fallible-blocks/7121/49)
|
||
|
||
However,
|
||
|
||
1. There are still plenty of materials out there which mention `try!`.
|
||
2. `try!` is essentially the inverse of `try { .. }`.
|
||
|
||
> Purging from the “collective memories of Rustaceans and Rust materials” is not something that easy.
|
||
|
||
\- [Manish Goregaokar](https://internals.rust-lang.org/t/bikeshed-rename-catch-blocks-to-fallible-blocks/7121/50)
|
||
|
||
In the RFC author's opinion however, the sum total benefits of `try { .. }`
|
||
seem to outweigh the drawbacks of the difficulty with purging [`try!`] from
|
||
our collective memory.
|
||
|
||
## Inverse semantics of `?`
|
||
|
||
The `?` postfix operator is sometimes referred to as the "try operator",
|
||
and can be seen as having the inverse semantics as `try { .. }`.
|
||
|
||
To many, this is a drawback. To others, this makes the `?` and `try { .. }`
|
||
expression forms more closely related and therefore makes them more findable
|
||
in relation to each other.
|
||
|
||
There is currently some ongoing debate about renaming the `?` operator to
|
||
something other than the "try operator". This could help in mitigating the
|
||
effects of picking `try` as the keyword.
|
||
|
||
# Rationale and alternatives
|
||
[rationale-and-alternatives]: #rationale-and-alternatives
|
||
|
||
## Review considerations
|
||
|
||
Among the considerations when picking a keyword are, ordered by importance:
|
||
|
||
1. Fidelity to the construct's actual behavior.
|
||
|
||
2. Precedent from existing languages
|
||
1. Popularity of the languages.
|
||
2. Fidelity to behavior in those languages.
|
||
3. Familiarity with respect to their analogous constructs.
|
||
|
||
See the [prior art][prior-art] the [rationale for try]
|
||
for more discussion on precedent.
|
||
|
||
2. Brevity.
|
||
|
||
[`Try`]: https://doc.rust-lang.org/nightly/std/ops/trait.Try.html
|
||
|
||
4. Consistency with related standard library function conventions.
|
||
|
||
5. Consistency with the naming of the trait used for `?` (the [`Try`] trait).
|
||
Since the `Try` trait is unstable and the naming of the `?` operator in
|
||
communication is still unsettled, this is not regarded as very important.
|
||
|
||
6. Degree / Risk of breakage.
|
||
|
||
7. Consistency with old learning material.
|
||
|
||
1. Inversely: The extent of the old learning material
|
||
|
||
That is, (in)consistency with `?` and the `try!()` macro.
|
||
If the first clause is called `try`,
|
||
then `try { }` and `try!()` would have essentially inverse meanings.
|
||
|
||
## Rationale for `try`
|
||
[rationale for try]: #rationale-for-try
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Very high
|
||
2. **Precedent from existing languages:** A lot, see [prior-art]
|
||
1. **Popularity of the languages:** Massive accumulated dominance
|
||
2. **Fidelity to behavior in those languages:** Very high
|
||
3. **Familiarity with respect to their analogous constructs:** Very high
|
||
3. **Brevity / Length:** 3
|
||
4. **Consistency with related libstd fn conventions:** Consistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Consistent
|
||
6. **Risk of breakage:** High (if we assume `try!` will break, otherwise: Low)
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=try), `std::try!`, but it is technically possible to not break this macro. (unstable: `std::intrinsics::try` so irrelevant)
|
||
- **Used as crate?** [*Yes*](https://crates.io/crates/try). No reverse dependencies. Described as: *"Deprecation warning resistant try macro"*
|
||
- **Usage (sourcegraph):** **27** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+try\s+=|(fn|impl|mod|struct|enum|union|trait)\s+try)\b
|
||
```
|
||
7. **Consistency with old learning material:** Inconsistent ([`try!`])
|
||
|
||
### Review
|
||
|
||
This is our choice of keyword, because it:
|
||
|
||
1. has a massive dominance in both popular and less known languages and is
|
||
sufficiently semantically faithful to what `try` means in those languages.
|
||
Thus, we can leverage people's intuitions and not spend too much of our
|
||
complexity budget.
|
||
2. is consistent with the standard library wrt. `Try` and `try_` prefixed methods.
|
||
3. it is brief.
|
||
4. it has high fidelity wrt. the concepts it attempts to communicate
|
||
(exception boundary for `?`). This high fidelity is from the perspective of
|
||
a programmers intent, i.e: "I want to try a bunch of stuff in this block".
|
||
5. it can be further extended with `catch { .. }` handlers if we wish.
|
||
|
||
## Alternative: reserving `catch`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** High
|
||
2. **Precedent from existing languages:** Erlang and Tcl, see [prior-art]
|
||
3. **Brevity / Length:** 6
|
||
4. **Consistency with related libstd fn conventions:** Somewhat consistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Low
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=catch)
|
||
- **Used as crate?** [*No*](https://crates.io/crates/catch).
|
||
- **Usage (sourcegraph):** **21** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+catch\s+=|(fn|impl|mod|struct|enum|union|trait)\s+catch)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
### Review
|
||
|
||
We believe `catch` to be a poor choice of keyword, because it:
|
||
|
||
1. is used in few in other languages to demarcate the body which can result in
|
||
an exceptional path. Instead, it is almost exclusively used for exception
|
||
handlers of the form: `catch(pat) { recover_expr }`.
|
||
4. extending `catch` with handlers will require a different word such as
|
||
`handler` to get `catch { .. } handler(e) { .. }` semantics if we want.
|
||
This inversion compared to a lot of other languages will only harm
|
||
teachability of the language and steal a lot of our strangeness budget.
|
||
2. it is less brief than `try`.
|
||
3. the consistency wrt. methods in the standard library is low -
|
||
there's only `catch_unwind`, but that has to do with panics,
|
||
not `Try` style exceptions.
|
||
|
||
However, `catch` has high fidelity wrt. the operational semantics of "catching"
|
||
any exceptions in the `try { .. }` block.
|
||
|
||
## Alternative: keeping `do catch { .. }`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Middle
|
||
2. **Precedent from existing languages:**
|
||
+ `do`: Haskell, Idris
|
||
+ `catch`: Erlang and Tcl, see [prior-art]
|
||
3. **Brevity / Length:** 8
|
||
4. **Consistency with related libstd fn conventions:** Tiny bit consistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Impossible (already reserved keyword)
|
||
- **Used in std:** *No*, the form `$ident $ident` is not a legal identifier.
|
||
- **Used as crate?** *No*, as above.
|
||
- **Usage (sourcegraph):** **0** regex: N/A
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
An alternative would be to simply use the `do catch { ... }` syntax we have
|
||
in the nightly compiler. However, this syntax was not in the accepted `catch`
|
||
RFC and was only a temporary fix around `catch { .. }` not working.
|
||
|
||
## Alternative: `do try { .. }`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** High
|
||
2. **Precedent from existing languages:**
|
||
+ `do`: Haskell, Idris
|
||
+ `try`: A lot, see [prior-art]
|
||
1. **Popularity of the languages:** Massive accumulated dominance
|
||
2. **Fidelity to behavior in those languages:** High
|
||
3. **Familiarity with respect to their analogous constructs:** High
|
||
3. **Brevity / Length:** 6 (including space)
|
||
4. **Consistency with related libstd fn conventions:** Moderately consistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Moderately consistent
|
||
6. **Risk of breakage:** Impossible (already reserved keyword)
|
||
- **Used in std:** *No*, the form `$ident $ident` is not a legal identifier.
|
||
- **Used as crate?** *No*, as above.
|
||
- **Usage (sourcegraph):** **0** regex: N/A
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
### Review
|
||
|
||
We could in fact decide to keep the `do`-prefix but change the suffix to `try`.
|
||
The benefit here would be two-fold:
|
||
|
||
+ No keyword `try` would need to be introduced as `do` already is a keyword.
|
||
Therefore, the `try!` macro would not break.
|
||
|
||
+ An association with monads due to `do`. This can be considered a benfit since
|
||
`try` can be seen as sugar for the family of error monads
|
||
(modulo kinks wrt. imperative flow), and thus,
|
||
the `do` prefix leads to a path of generality if more monads are introduced.
|
||
|
||
The drawbacks would be:
|
||
|
||
+ The wider association with monads can be seen as a drawback for those not
|
||
familiar with monads.
|
||
|
||
+ `do try { .. }` over `try { .. }` adds a small degree of ergonomics overhead
|
||
but not much (3 characters including the space). However, the frequency with
|
||
which the `try { .. }` construct might be used can make the small overhead
|
||
accumulate to a significant overhead when a large codebase is considered.
|
||
|
||
Other than this, the argument for `do try` over `do catch` boils down to an
|
||
argument of `try` over `catch`.
|
||
|
||
## Alternative: using `do { .. }`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Not at all.
|
||
2. **Precedent from existing languages:** Haskell, Idris
|
||
1. **Popularity of the languages:** Haskell: Tiobe #42, PYPL #22
|
||
2. **Fidelity to behavior in those languages:** Good
|
||
3. **Familiarity with respect to their analogous constructs:** Poor
|
||
3. **Brevity / Length:** 2
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Impossible (already reserved keyword)
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
### Review
|
||
|
||
The keyword `do` was probably originally reserved for two use cases:
|
||
|
||
1. `do while { .. }`
|
||
|
||
2. Monadic `do`-notation a la Haskell:
|
||
|
||
```haskell
|
||
stuff = do
|
||
x <- actionX
|
||
y <- actionY x
|
||
z <- actionZ
|
||
sideEffect
|
||
finalAction x y z
|
||
```
|
||
|
||
The which would be translated into the following pseudo-Rust:
|
||
|
||
```rust
|
||
let stuff = do {
|
||
x <- actionX;
|
||
y <- actionY(x);
|
||
z <- actionZ;
|
||
sideEffect;
|
||
finalAction(x, y, z);
|
||
};
|
||
```
|
||
|
||
Or particularly for the `try { .. }` case:
|
||
|
||
```rust
|
||
let stuff = try {
|
||
let x = actionX?;
|
||
let y = actionY(x)?;
|
||
let z = actionZ?;
|
||
sideEffect?;
|
||
finalAction(x, y, z)
|
||
};
|
||
```
|
||
|
||
The Haskell version is syntactic sugar for:
|
||
|
||
```haskell
|
||
stuff = actionX >>=
|
||
\x -> actionY x >>=
|
||
\y -> actionZ >>=
|
||
\z -> sideEffect >>
|
||
finalAction x y z
|
||
```
|
||
|
||
or in Rust:
|
||
|
||
```rust
|
||
let stuff =
|
||
actionX.flat_map(|x| // or .and_then(..)
|
||
actionY(x).flat_map(|y|
|
||
actionZ.flat_map(|z|
|
||
sideEffect.flat_map(|_|
|
||
finalAction(x, y, z)
|
||
)
|
||
)
|
||
)
|
||
);
|
||
```
|
||
|
||
In the Haskell version, `>>=` is defined in the `Monad` typeclass (trait):
|
||
|
||
```haskell
|
||
{-# LANGUAGE KindSignatures #-}
|
||
|
||
class Applicative m => Monad (m :: * -> *) where
|
||
return :: a -> m a
|
||
(>>=) :: m a -> (a -> m b) -> m b
|
||
|
||
(>>) :: m a -> m b -> m b
|
||
(>>) = \ma mb -> ma >>= \_ -> mb
|
||
```
|
||
|
||
And some instances (impls) of `Monad` are:
|
||
|
||
```haskell
|
||
-- | Same as Option<T>
|
||
data Maybe a = Nothing | Just a
|
||
|
||
instance Monad Maybe where
|
||
return = Just
|
||
(Just a) >>= f = f a
|
||
_ >>= _ = Nothing
|
||
|
||
-- | `struct Norm<T> { value: T, normalized: bool }`
|
||
data Norm a = Norm a Bool
|
||
|
||
instance Monad Norm where
|
||
return a = Norm a False
|
||
(Norm a u) >>= f = let Norm b w = f a in Norm b (u || w)
|
||
```
|
||
|
||
[`MonadError`]: http://hackage.haskell.org/package/mtl-2.2.2/docs/Control-Monad-Error-Class.html#t:MonadError
|
||
|
||
Considering the latter case of do-notation,
|
||
we saw how `try { .. }` and `do { .. }` relate.
|
||
In fact, `try { .. }` is special to the [`Try`] ([`MonadError`]) monads.
|
||
There are also more forms of monads which you might want to use `do { .. }` for.
|
||
Among these are: Futures, Iterators
|
||
Due to having more monads than [`Try`]-based ones,
|
||
using the `do { .. }` syntax directly as a replacement for `try { .. }` becomes
|
||
problematic as it:
|
||
|
||
1. confuses everyone familiar with do-notation and monads.
|
||
2. is in the way of use for monads in general.
|
||
3. `do` is generic and unclear wrt. semantics.
|
||
|
||
## Alternative: reserving `trap`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Good
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 4
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Very low
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=trap)
|
||
- **Used as crate?** [*No*](https://crates.io/crates/trap).
|
||
- **Usage (sourcegraph):** **4** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+trap\s+=|(fn|impl|mod|struct|enum|union|trait)\s+trap)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
### Review
|
||
|
||
Arguably, this candidate keyword is a somewhat a good choice.
|
||
|
||
To `trap` an error is sufficiently clear on the "exception boundary" semantics
|
||
we wish to communicate.
|
||
|
||
However, `trap` is used as an error handler in at least one language.
|
||
|
||
It also does not have the familiarity that `try` does have and is entirely
|
||
inconsistent wrt. naming in the standard library.
|
||
|
||
## Alternative: reserving `wrap`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Somewhat good
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 4
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Very low
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=wrap)
|
||
- **Used as crate?** [*Yes*](https://crates.io/crates/wrap), no reverse dependencies.
|
||
- **Usage (sourcegraph):** **37+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+wrap\s+=|(fn|impl|mod|struct|enum|union|trait)\s+wrap)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
### Review
|
||
|
||
With `wrap { .. }` we can say that it "wraps" the result of the block as a
|
||
`Result` / `Option`, etc. and it is logically related to `.unwrap()`,
|
||
which is however a partial function, wherefore the connotation might be bad.
|
||
|
||
Also, `wrap` could be considered too generic as with `do` in that it could
|
||
fit for any monad.
|
||
|
||
## Alternative: reserving `result`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Somewhat good
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 6
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Very high
|
||
- **Used in std:** [*Yes*](https://doc.rust-lang.org/nightly/std/?search=result) for the `{std, core}::result` modules.
|
||
- **Used as crate?** [*Yes*](https://crates.io/crates/result). 6 reverse dependencies (transitive closure).
|
||
- **Usage (sourcegraph):** **43+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+result\s+=|(fn|impl|mod|struct|enum|union|trait)\s+result)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
## Review
|
||
|
||
[final encoding]: http://okmij.org/ftp/tagless-final/course/lecture.pdf
|
||
|
||
The fidelity of `result` is somewhat good due to the association with the
|
||
`Result` type as well as `Try` being a [final encoding] of `Result`.
|
||
|
||
However, when you consider `Option`, the association is less direct,
|
||
and thus it does not fit `Option` and other types well.
|
||
|
||
The breakage of the `result` module is however quite problematic,
|
||
making this particular choice of keyword more or less a non-starter.
|
||
|
||
## Alternative: a smattering of other possible keywords
|
||
|
||
There are a host of other keywords which have been suggested.
|
||
|
||
### `fallible`
|
||
|
||
On an [internals thread](https://internals.rust-lang.org/t/bikeshed-rename-catch-blocks-to-fallible-blocks/7121/), `fallible` was suggested. However, this keyword lacks the verb-form that
|
||
is the convention in Rust. Breaking with this convention should only be done
|
||
if there are significant reasons to do so, which do not seem to exist in this
|
||
case. It is also considerably longer than `try` (+5 character) which matters
|
||
for constructions which are oft used.
|
||
|
||
1. **Fidelity to the construct's actual behavior:** High
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 8
|
||
4. **Consistency with related libstd fn conventions:** Highly inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Very low
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=fallible)
|
||
- **Used as crate?** [*Yes*](https://crates.io/crates/fallible), some reverse dependencies (all by the same author).
|
||
- **Usage (sourcegraph)** [*None*](https://sourcegraph.com/search?q=repogroup:crates+case:yes++%5Cb%28%28let%7Cconst%7Ctype%7C%29%5Cs%2Bfallible%5Cs%2B%3D%7C%28fn%7Cimpl%7Cmod%7Cstruct%7Cenum%7Cunion%7Ctrait%29%5Cs%2Bfallible%29%5Cb+max:400)
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
### Synonyms of `catch`:
|
||
|
||
Some synonyms of `catch` [have been suggested](https://internals.rust-lang.org/t/bikeshed-rename-catch-blocks-to-fallible-blocks/7121/2):
|
||
|
||
#### `accept`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Not at all.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 6
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Medium
|
||
- **Used in std:** [*Yes*](https://doc.rust-lang.org/nightly/std/?search=accept)
|
||
- **Used as crate?** [*No*](https://crates.io/crates/accept).
|
||
- **Usage (sourcegraph):** **79+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+accept\s+=|(fn|impl|mod|struct|enum|union|trait)\s+accept)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `capture`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Good.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 7
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Low
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=capture)
|
||
- **Used as crate?** [*Yes*](https://crates.io/crates/capture), no reverse dependencies.
|
||
- **Usage (sourcegraph):** **6+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+capture\s+=|(fn|impl|mod|struct|enum|union|trait)\s+capture)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `collect`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Very much not at all.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 7
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Very high
|
||
- **Used in std:** [*Yes*](https://doc.rust-lang.org/nightly/std/?search=collect) (`Iterator::collect`)
|
||
- **Used as crate?** [*Yes*](https://crates.io/crates/collect), no reverse dependencies.
|
||
- **Usage (sourcegraph):** **35+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+collect\s+=|(fn|impl|mod|struct|enum|union|trait)\s+collect)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `recover`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Good
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 7
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Very low
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=recover)
|
||
- **Used as crate?** [*No*](https://crates.io/crates/recover)
|
||
- **Usage (sourcegraph):** **4+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+recover\s+=|(fn|impl|mod|struct|enum|union|trait)\s+recover)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `resolve`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Not at all.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 7
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Low to medium
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=resolve)
|
||
- **Used as crate?** [*Yes*](https://crates.io/crates/resolve), 3 reverse dependencies
|
||
- **Usage (sourcegraph):** **50+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+resolve\s+=|(fn|impl|mod|struct|enum|union|trait)\s+resolve)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `take`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Not at all.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 4
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Huge
|
||
- **Used in std:** [*Yes*](https://doc.rust-lang.org/nightly/std/?search=take), `{Cell, HashSet, Read, Iterator, Option}::take`.
|
||
- **Used as crate?** [*Yes*](https://crates.io/crates/resolve), a lot of reverse dependency (transitive closure).
|
||
- **Usage (sourcegraph):** **62+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+take\s+=|(fn|impl|mod|struct|enum|union|trait)\s+take)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### Review
|
||
|
||
Of these, only `recover` and `capture` seem reasonable semantically.
|
||
But `recover` is even more problematic than `catch` because it enhances
|
||
the feeling of exception-handling instead of exception-boundaries.
|
||
However, `capture` is reasonable as a substitute for `try`,
|
||
but it seems obscure and lacks familiarity, which is counted as a strong downside.
|
||
|
||
### [and some other keywords:](https://internals.rust-lang.org/t/bikeshed-rename-catch-blocks-to-fallible-blocks/7121/13)
|
||
|
||
#### `coalesce`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Not at all.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 8
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Medium (itertools)
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=coalesce).
|
||
- **Used as crate?** [*Yes*](https://crates.io/crates/coalesce), one reverse dependency.
|
||
- **Usage (sourcegraph):** **3+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+coalesce\s+=|(fn|impl|mod|struct|enum|union|trait)\s+coalesce)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `fuse`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Not at all.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 4
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Medium (libstd)
|
||
- **Used in std:** [*Yes*](https://doc.rust-lang.org/nightly/std/?search=fuse), `Iterator::fuse`.
|
||
- **Used as crate?** [*Yes*](https://crates.io/crates/fuse), 8 reverse dependencies (transitive closure).
|
||
- **Usage (sourcegraph):** **8+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+fuse\s+=|(fn|impl|mod|struct|enum|union|trait)\s+fuse)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `unite`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Not at all.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 5
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Very low
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=unite).
|
||
- **Used as crate?** [*No*](https://crates.io/crates/unite).
|
||
- **Usage (sourcegraph):** **0+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+unite\s+=|(fn|impl|mod|struct|enum|union|trait)\s+unite)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `cohere`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Not at all.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 6
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Very low
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=cohere).
|
||
- **Used as crate?** [*No*](https://crates.io/crates/cohere).
|
||
- **Usage (sourcegraph):** **0+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+cohere\s+=|(fn|impl|mod|struct|enum|union|trait)\s+cohere)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `consolidate`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Not at all.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 11
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Very low
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=consolidate).
|
||
- **Used as crate?** [*No*](https://crates.io/crates/consolidate).
|
||
- **Usage (sourcegraph):** **0+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+consolidate\s+=|(fn|impl|mod|struct|enum|union|trait)\s+consolidate)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `unify`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Not at all.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 5
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Very low
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=unify).
|
||
- **Used as crate?** [*Yes*](https://crates.io/crates/unify), no dependencies
|
||
- **Usage (sourcegraph):** **1** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+take\s+=|(fn|impl|mod|struct|enum|union|trait)\s+take)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `combine`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Not at all.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 7
|
||
4. **Consistency with related libstd fn conventions:** Inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Medium
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=combine).
|
||
- **Used as crate?** [*Yes*](https://crates.io/crates/combine), 17 (direct dependencies)
|
||
- **Usage (sourcegraph):** **6+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+combine\s+=|(fn|impl|mod|struct|enum|union|trait)\s+combine)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `resultof`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Somewhat
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 8
|
||
4. **Consistency with related libstd fn conventions:** Very inconsistent (not verb)
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:** Very low
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=resultof).
|
||
- **Used as crate?** [*No*](https://crates.io/crates/resultof).
|
||
- **Usage (sourcegraph):** **0+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+resultof\s+=|(fn|impl|mod|struct|enum|union|trait)\s+resultof)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### `returned`
|
||
|
||
1. **Fidelity to the construct's actual behavior:** Not at all.
|
||
2. **Precedent from existing languages:** None
|
||
3. **Brevity / Length:** 8
|
||
4. **Consistency with related libstd fn conventions:** Very inconsistent
|
||
5. **Consistency with the naming of the trait used for `?`:** Inconsistent
|
||
6. **Risk of breakage:**
|
||
- **Used in std:** [*No*](https://doc.rust-lang.org/nightly/std/?search=returned).
|
||
- **Used as crate?** [*No*](https://crates.io/crates/returned).
|
||
- **Usage (sourcegraph):** **0+** regex:
|
||
```
|
||
repogroup:crates case:yes max:400
|
||
\b((let|const|type|)\s+returned\s+=|(fn|impl|mod|struct|enum|union|trait)\s+returned)\b
|
||
```
|
||
7. **Consistency with old learning material:** Untaught
|
||
|
||
#### Review
|
||
|
||
Of these, only `resultof` seems to be semantically descriptive and has some support. However, it has three major drawbacks:
|
||
|
||
+ Length: Compared to `try`, it is 5 characters longer (see reasoning for `fallible`).
|
||
|
||
+ Not a word: `resultof` is in fact a concatenation of `result` and `of`.
|
||
This does not feel like a natural fit for Rust, as we tend to use a `_` separator.
|
||
Furthermore, there are no current keywords in use that are concatenations of two word.
|
||
|
||
+ `Result<T, E>` oriented: `resultof` is too tied to `Result<T, E>` and fits poorly with `Option<T>` or other types that implement `Try`.
|
||
|
||
# Prior art
|
||
[prior-art]: #prior-art
|
||
|
||
All of the languages listed below have a `try { .. } <handler_kw> { .. }` concept
|
||
(modulo layout syntax / braces) where `<handler_kw>` is one of:
|
||
`catch`, `with`, `except`, `trap`, `rescue`.
|
||
|
||
In total, these are 29 languages and they have massive ~80% dominance according
|
||
to the [TIOBE index](https://www.tiobe.com/tiobe-index/)
|
||
and roughly the same with the [PYPL index](http://pypl.github.io/PYPL.html).
|
||
|
||
+ [C++](http://en.cppreference.com/w/cpp/language/try_catch)
|
||
+ [D](https://tour.dlang.org/tour/en/basics/exceptions)
|
||
+ [C#](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/try-catch)
|
||
+ [Java](https://docs.oracle.com/javase/tutorial/essential/exceptions/try.html)
|
||
+ [Scala](https://stackoverflow.com/questions/18685573/try-catch-finally-return-value)
|
||
+ [Kotlin](https://kotlinlang.org/docs/reference/exceptions.html)
|
||
+ [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch)
|
||
+ [TypeScript](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-5.html)
|
||
+ [ActionScript](https://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7ed1.html#WS5b3ccc516d4fbf351e63e3d118a9b90204-7ec5)
|
||
+ [Dart](https://www.dartlang.org/resources/dart-tips/dart-tips-ep-9)
|
||
+ [Python](https://docs.python.org/3/tutorial/errors.html)
|
||
+ [PHP](http://php.net/manual/en/language.exceptions.php)
|
||
+ [Matlab](https://se.mathworks.com/help/matlab/ref/try.html)
|
||
+ [Visual Basic](https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/statements/try-catch-finally-statement)
|
||
+ [OCaml](https://ocaml.org/learn/tutorials/error_handling.html)
|
||
+ [F#](https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/exception-handling/the-try-with-expression)
|
||
+ [Objective C](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/ErrorHandling/ErrorHandling.html#//apple_ref/doc/uid/TP40011210-CH9-SW3)
|
||
+ [Swift](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html)
|
||
+ [Delphi](https://stackoverflow.com/questions/6601147/how-to-correctly-write-try-finally-except-statements)
|
||
+ [Julia](https://docs.julialang.org/en/stable/manual/control-flow/#The-try/catch-statement-1)
|
||
+ [Elixir](https://elixir-lang.org/getting-started/try-catch-and-rescue.html)
|
||
+ [Erlang](http://erlang.org/doc/reference_manual/expressions.html#try)
|
||
+ [Clojure](https://clojuredocs.org/clojure.core/try)
|
||
+ [R](https://www.rdocumentation.org/packages/base/versions/3.0.3/topics/conditions), modulo minor syntactic difference.
|
||
+ [Powershell](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_try_catch_finally?view=powershell-6)
|
||
+ [Tcl](http://wiki.tcl.tk/8293)
|
||
+ [Apex](https://developer.salesforce.com/page/An_Introduction_to_Exception_Handling)
|
||
+ [RPG](http://devnet.asna.com/documentation/Help102/AVR/_HTML/TRYCATCHFINALLY.htm)
|
||
+ [ABAP](https://help.sap.com/doc/abapdocu_751_index_htm/7.51/en-US/abaptry.htm)
|
||
|
||
The syntactic form `catch { .. }` seems quite rare and is,
|
||
together with `trap`, `rescue`, `except`, only used for handlers.
|
||
However, the `<kw> { .. }` expression we want to introduce is not a handler,
|
||
but rather the body of expression we wish to `try`.
|
||
|
||
There are however a few languages where `catch { .. }` is used for the fallible
|
||
part and not for the handler, these languages are:
|
||
+ [Erlang](http://erlang.org/doc/reference_manual/expressions.html#catch)
|
||
+ [Tcl](https://www.tcl.tk/man/tcl/TclCmd/catch.htm)
|
||
|
||
However, the combined popularity of these languages are not significant as
|
||
compared to that for `try { .. }`.
|
||
|
||
# Unresolved questions
|
||
[unresolved]: #unresolved-questions
|
||
|
||
None as of yet.
|