From e1018547cd5eabf40cf229cb2fa26e29a795cf3d Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sun, 12 Apr 2026 12:02:05 +0000 Subject: [PATCH 01/24] Intro & Motivation --- text/0000-try-blocks-heterogeneous.md | 198 ++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 text/0000-try-blocks-heterogeneous.md diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md new file mode 100644 index 00000000000..744c0f9a62c --- /dev/null +++ b/text/0000-try-blocks-heterogeneous.md @@ -0,0 +1,198 @@ +- Feature Name: `try_blocks_heterogeneous` +- Start Date: 2026-04-12 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +## Summary +[summary]: #summary + +[RFC 3721](https://github.com/rust-lang/rfcs/pull/3721) implemented default support for homogeneous `try {...}` blocks, where all `?` return the same error type. This RFC aims to provide support for explicit annotation of the returned error type from a `try {...}` block. + +## Motivation +[motivation]: #motivation + +> I'm a bit concerned about this change. Applications and libraries often use crates like `thiserror` to automatically group errors. For example, I often write something like +> +> ```rust +> #[derive(Error)] +> enum MyError { +> #[error("Failed to parse config: {0}")] +> InvalidConfig(#[from] serde::Error), +> #[error("Failed to connect to server: {0}")] +> ServerConnectionFailed(#[from] io::Error), +> ... +> } +> ``` +> +> which I then use as +> +> ```rust +> fn example() -> Result<(), MyError> { +> let config = parse_config()?; // ? promotes serde::Error to MyError +> let server = connect_to_server(server.url)?; // ? promotes io::Error to MyError +> // ... +> } +> ``` +> +> With this change, this approach would stop working in `try` blocks. +> +> ~ [purplesyringa commenting on #3721](https://github.com/rust-lang/rfcs/pull/3721#issuecomment-2466852085) + +Currently there is no way to get the following example to compile as the compiler is unable to safely determine the correct types returned from the try blocks and no notation available for the user to specify the type: + +```rust +#![feature(try_blocks)] + +use std::num::ParseIntError; + +#[derive(Debug)] +struct Error1; + +#[derive(Debug)] +struct Error2; + +impl From for Error1 { + fn from(_: ParseIntError) -> Self { + Self + } +} + +impl From for Error2 { + fn from(_: ParseIntError) -> Self { + Self + } +} + +impl From for Error2 { + fn from(_: Error2) -> Self { + Self + } +} + +impl From for Error1 { + fn from(_: Error2) -> Self { + Self + } +} + +fn err1(s: &str) -> Result { + Ok(s.parse()?) +} + +fn err2(s: &str) -> Result { + Ok(s.parse()?) +} + +fn heterogeneous_into_exists() { + let x = try { err1("1")? + err2("2")? }; + let y = try { err2("1")? + err1("2")? }; + assert_eq!(x.unwrap(), y.unwrap()); +} +``` + +The initial experimental approach to provide a prrof-of-concept introduced the (deliberate placeholder) syntax `try bikeshed ... {...}` in [PR #149489](https://github.com/rust-lang/rust/pull/149489). + +_For the remainder of this RFC we will continue with `bikeshed` to allow for working examples. See open questions and [try bikeshed: What should the syntax be?](https://github.com/rust-lang/rust/issues/154128) for consideration of possible target syntax._ + +This would allow the above example to become: + +```rust +fn heterogeneous_into_exists() { + let x = try bikeshed Result<_, Error1> { err1("1")? + err2("2")? }; + let y = try bikeshed Result<_, Error1> { err2("1")? + err1("2")? }; + assert_eq!(x.unwrap(), y.unwrap()); +} +``` + +and for cases where no direct `Into` relationship exists, or is needed, via a common third error type: + +```rust +fn heterogeneous_into_exists() { + let x = try bikeshed anyhow::Result<_> { err1("1")? + err2("2")? }; + let y = try bikeshed anyhow::Result<_> { err2("1")? + err1("2")? }; + assert_eq!(x.unwrap(), y.unwrap()); +} +``` + +## Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +Explain the proposal as if it was already included in the language and you were teaching it to another Rust programmer. That generally means: + +- Introducing new named concepts. +- Explaining the feature largely in terms of examples. +- Explaining how Rust programmers should *think* about the feature, and how it should impact the way they use Rust. It should explain the impact as concretely as possible. +- If applicable, provide sample error messages, deprecation warnings, or migration guidance. +- If applicable, describe the differences between teaching this to existing Rust programmers and new Rust programmers. +- Discuss how this impacts the ability to read, understand, and maintain Rust code. Code is read and modified far more often than written; will the proposed feature make code easier to maintain? + +For implementation-oriented RFCs (e.g. for compiler internals), this section should focus on how compiler contributors should think about the change, and give examples of its concrete impact. For policy RFCs, this section should provide an example-driven introduction to the policy, and explain its impact in concrete terms. + +## Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +This is the technical portion of the RFC. Explain the design in sufficient detail that: + +- Its interaction with other features is clear. +- It is reasonably clear how the feature would be implemented. +- Corner cases are dissected by example. + +The section should return to the examples given in the previous section, and explain more fully how the detailed proposal makes those examples work. + +## Drawbacks +[drawbacks]: #drawbacks + +Why should we *not* do this? + +## Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +- Why is this design the best in the space of possible designs? +- What other designs have been considered and what is the rationale for not choosing them? +- What is the impact of not doing this? +- If this is a language proposal, could this be done in a library or macro instead? Does the proposed change make Rust code easier or harder to read, understand, and maintain? + +## Prior art +[prior-art]: #prior-art + +Discuss prior art, both the good and the bad, in relation to this proposal. +A few examples of what this can include are: + +- For language, library, cargo, tools, and compiler proposals: Does this feature exist in other programming languages and what experience have their community had? +- For community proposals: Is this done by some other community and what were their experiences with it? +- For other teams: What lessons can we learn from what other communities have done here? +- Papers: Are there any published papers or great posts that discuss this? If you have some relevant papers to refer to, this can serve as a more detailed theoretical background. + +This section is intended to encourage you as an author to think about the lessons from other languages, provide readers of your RFC with a fuller picture. +If there is no prior art, that is fine - your ideas are interesting to us whether they are brand new or if it is an adaptation from other languages. + +Note that while precedent set by other languages is some motivation, it does not on its own motivate an RFC. +Please also take into consideration that rust sometimes intentionally diverges from common language features. + +## Unresolved questions +[unresolved-questions]: #unresolved-questions + +- What parts of the design do you expect to resolve through the RFC process before this gets merged? +- What parts of the design do you expect to resolve through the implementation of this feature before stabilization? +- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? + +## Future possibilities +[future-possibilities]: #future-possibilities + +Think about what the natural extension and evolution of your proposal would +be and how it would affect the language and project as a whole in a holistic +way. Try to use this section as a tool to more fully consider all possible +interactions with the project and language in your proposal. +Also consider how this all fits into the roadmap for the project +and of the relevant sub-team. + +This is also a good place to "dump ideas", if they are out of scope for the +RFC you are writing but otherwise related. + +If you have tried and cannot think of any future possibilities, +you may simply state that you cannot think of anything. + +Note that having something written down in the future-possibilities section +is not a reason to accept the current or a future RFC; such notes should be +in the section on motivation or rationale in this or subsequent RFCs. +The section merely provides additional information. From 2b73f681333977d94cbe920d850d90ef46623159 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sun, 12 Apr 2026 12:17:20 +0000 Subject: [PATCH 02/24] unresolved questions: rust-lang/rust#154127 rust-lang/rust#154128 --- text/0000-try-blocks-heterogeneous.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 744c0f9a62c..c18577e95cb 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -172,9 +172,8 @@ Please also take into consideration that rust sometimes intentionally diverges f ## Unresolved questions [unresolved-questions]: #unresolved-questions -- What parts of the design do you expect to resolve through the RFC process before this gets merged? -- What parts of the design do you expect to resolve through the implementation of this feature before stabilization? -- What related issues do you consider out of scope for this RFC that could be addressed in the future independently of the solution that comes out of this RFC? +- [ ] What should the syntax be? See [Issue #154128](https://github.com/rust-lang/rust/issues/154128) for discussion of alternatives (`:`, `->`, `as`, ...) +- [ ] What type should be annotated? This should probably be the full type, with optional inferance, as currently implemented for `bikeshed`, but see [Issue #154127](https://github.com/rust-lang/rust/issues/154127) for discussion. ## Future possibilities [future-possibilities]: #future-possibilities From 70fb739e091e9a608304031b0bca291e037616f1 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sun, 12 Apr 2026 12:22:20 +0000 Subject: [PATCH 03/24] explicit anyhow example --- text/0000-try-blocks-heterogeneous.md | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index c18577e95cb..aa1934c0efb 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -107,7 +107,24 @@ fn heterogeneous_into_exists() { and for cases where no direct `Into` relationship exists, or is needed, via a common third error type: ```rust -fn heterogeneous_into_exists() { +use std::{error::Error, fmt::Display}; +impl Error for Error1 {} +impl Display for Error1 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Error1")?; + Ok(()) + } +} + +impl Error for Error2 {} +impl Display for Error2 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Error2")?; + Ok(()) + } +} + +fn heterogeneous_into_anyhow() { let x = try bikeshed anyhow::Result<_> { err1("1")? + err2("2")? }; let y = try bikeshed anyhow::Result<_> { err2("1")? + err1("2")? }; assert_eq!(x.unwrap(), y.unwrap()); @@ -121,7 +138,7 @@ Explain the proposal as if it was already included in the language and you were - Introducing new named concepts. - Explaining the feature largely in terms of examples. -- Explaining how Rust programmers should *think* about the feature, and how it should impact the way they use Rust. It should explain the impact as concretely as possible. +- Explaining how Rust programmers should _think_ about the feature, and how it should impact the way they use Rust. It should explain the impact as concretely as possible. - If applicable, provide sample error messages, deprecation warnings, or migration guidance. - If applicable, describe the differences between teaching this to existing Rust programmers and new Rust programmers. - Discuss how this impacts the ability to read, understand, and maintain Rust code. Code is read and modified far more often than written; will the proposed feature make code easier to maintain? @@ -142,7 +159,7 @@ The section should return to the examples given in the previous section, and exp ## Drawbacks [drawbacks]: #drawbacks -Why should we *not* do this? +Why should we _not_ do this? ## Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From 562e7a75ab35b14973d20c68f9f702a1f588ea20 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sun, 12 Apr 2026 12:49:08 +0000 Subject: [PATCH 04/24] guide-level explanation --- text/0000-try-blocks-heterogeneous.md | 84 ++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 8 deletions(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index aa1934c0efb..7a3a50a0bd5 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -134,16 +134,84 @@ fn heterogeneous_into_anyhow() { ## Guide-level explanation [guide-level-explanation]: #guide-level-explanation -Explain the proposal as if it was already included in the language and you were teaching it to another Rust programmer. That generally means: +_Assuming the explanation for try blocks is implemented as per RFC 3712_ + +> This behaviour is what you want in the vast majority of simple cases. In particular, +> it always works for things with just one `?`, so simple things like `try { a? + 1 }` +> will do the right thing with minimal syntactic overhead. It's also common to want +> to group a bunch of things with the same error type. Perhaps it's a bunch of calls +> to one library, which all use that library's error type. Or you want to do +> [a bunch of `io` operations](> > compiler/rustc_borrowck/src/nll.rs#L355-L367) which all use `io::Result`. Additionally, `try` blocks work with +> `?`-on-`Option` as well, where error-conversion is never needed, since there is only `None`. +> +> It will fail to compile, however, if not everything shares the same error type. +> Suppose we add some formatting operation to the previous example: +> +> ```rust,edition2021,compile_fail +> let pair_result = try { +> let a = std::fs::read_to_string("hello")?; +> let b = std::fs::read_to_string("world")?; +> let c: i32 = b.parse()?; +> (a, c) +> }; +> ``` +> +> The compiler won't let us do that: +> +> ```text +> error[E0308]: mismatched types +> --> src/lib.rs:14:32 +> | +> | let c: i32 = b.parse()?; +> | ^ expected struct `std::io::Error`, found struct `ParseIntError` +> = note: expected enum `Result<_, std::io::Error>` +> found enum `Result<_, ParseIntError>` +> note: return type inferred to be `Result<_, std::io::Error>` here +> --> src/lib.rs:14:32 +> | +> | let a = std::fs::read_to_string("hello")?; +> | ^ +> ``` +> +> For now, the best solution for that mixed-error case is the same as before: to refactor it to a function. + +*replace the final sentence with ...* + +While it may be obvious, or even irrelevant, to you which error type pair_result could potentially have, the compiler has no way to know this. -- Introducing new named concepts. -- Explaining the feature largely in terms of examples. -- Explaining how Rust programmers should _think_ about the feature, and how it should impact the way they use Rust. It should explain the impact as concretely as possible. -- If applicable, provide sample error messages, deprecation warnings, or migration guidance. -- If applicable, describe the differences between teaching this to existing Rust programmers and new Rust programmers. -- Discuss how this impacts the ability to read, understand, and maintain Rust code. Code is read and modified far more often than written; will the proposed feature make code easier to maintain? +Just like in other situations where the compiler cannot safely infer the exact type to use, you must annotate the block with a valid error type. We've already mentioned that `Result` automatically converts between error types where a suitable implementation of `Into` exists and you can leverage this and write: + +```rust +let pair_result = try bikeshed Result<_, PairError> { + let a = std::fs::read_to_string("hello")?; + let b = std::fs::read_to_string("world")?; + let c: i32 = b.parse()?; + (a, c) +}; +``` + +As long as you have defined a suitable error: + +```rust +enum PairError { + IoError(Box), + ParseError(Box), +} + +impl From for PairError { + fn from(e: io::Error) -> Self { + Self::IoError(Box::new(e)) + } +} + +impl From for PairError { + fn from(e: num::ParseIntError) -> Self { + Self::ParseError(Box::new(e)) + } +} +``` -For implementation-oriented RFCs (e.g. for compiler internals), this section should focus on how compiler contributors should think about the change, and give examples of its concrete impact. For policy RFCs, this section should provide an example-driven introduction to the policy, and explain its impact in concrete terms. +Of course, there are crates available to simplify this if you do not want or need to create your own custom error type. ## Reference-level explanation [reference-level-explanation]: #reference-level-explanation From 1388bc0c0dbb6329f1924b17bafcc001dd47c9fa Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sun, 12 Apr 2026 12:58:39 +0000 Subject: [PATCH 05/24] future possibility - inference via function return type --- text/0000-try-blocks-heterogeneous.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 7a3a50a0bd5..63894a9f219 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -263,6 +263,23 @@ Please also take into consideration that rust sometimes intentionally diverges f ## Future possibilities [future-possibilities]: #future-possibilities +### Allow inference via function return type + +For cases such as + +```rust +fn heterogeneous_via_return_type() -> Result<(), Error1> { + let x = try { err1("1")? + err2("2")? }?; + let y = try { err2("1")? + err1("2")? }; + assert_eq!(x, y?); + Ok(()) +} +``` + +where the errors involved all implement `Into` + + + Think about what the natural extension and evolution of your proposal would be and how it would affect the language and project as a whole in a holistic way. Try to use this section as a tool to more fully consider all possible From c0b70961f96eb897ee5ec84df7742e5756bb9fae Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Sun, 12 Apr 2026 13:05:44 +0000 Subject: [PATCH 06/24] typos etc --- text/0000-try-blocks-heterogeneous.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 63894a9f219..c68456dc46d 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -6,7 +6,7 @@ ## Summary [summary]: #summary -[RFC 3721](https://github.com/rust-lang/rfcs/pull/3721) implemented default support for homogeneous `try {...}` blocks, where all `?` return the same error type. This RFC aims to provide support for explicit annotation of the returned error type from a `try {...}` block. +[RFC 3721](https://github.com/rust-lang/rfcs/pull/3721) implemented default support for homogeneous `try {...}` blocks, where all `?`s return the same error type. This RFC aims to provide support for explicit annotation of the returned error type from a `try {...}` block. ## Motivation [motivation]: #motivation @@ -38,7 +38,7 @@ > > ~ [purplesyringa commenting on #3721](https://github.com/rust-lang/rfcs/pull/3721#issuecomment-2466852085) -Currently there is no way to get the following example to compile as the compiler is unable to safely determine the correct types returned from the try blocks and no notation available for the user to specify the type: +Currently there is no way to get the following example to compile, as the compiler is unable to safely determine the correct types returned from the try blocks, and no notation is available for the user to specify the type: ```rust #![feature(try_blocks)] @@ -90,9 +90,11 @@ fn heterogeneous_into_exists() { } ``` -The initial experimental approach to provide a prrof-of-concept introduced the (deliberate placeholder) syntax `try bikeshed ... {...}` in [PR #149489](https://github.com/rust-lang/rust/pull/149489). +The initial experimental approach to provide a prrof-of-concept introduced the (**deliberate placeholder**) syntax `try bikeshed ... {...}` in [PR #149489](https://github.com/rust-lang/rust/pull/149489). -_For the remainder of this RFC we will continue with `bikeshed` to allow for working examples. See open questions and [try bikeshed: What should the syntax be?](https://github.com/rust-lang/rust/issues/154128) for consideration of possible target syntax._ +>_For the remainder of this RFC we will continue with `bikeshed` to allow for examples which work on current nightly with `#![feature(try_blocks_heterogeneous)]`._ +> +>_See open questions and [try bikeshed: What should the syntax be?](https://github.com/rust-lang/rust/issues/154128) for consideration of possible target syntax._ This would allow the above example to become: @@ -134,7 +136,7 @@ fn heterogeneous_into_anyhow() { ## Guide-level explanation [guide-level-explanation]: #guide-level-explanation -_Assuming the explanation for try blocks is implemented as per RFC 3712_ +_Assuming the explanation for try blocks is implemented as per RFC 3712, which contains:_ > This behaviour is what you want in the vast majority of simple cases. In particular, > it always works for things with just one `?`, so simple things like `try { a? + 1 }` @@ -177,7 +179,7 @@ _Assuming the explanation for try blocks is implemented as per RFC 3712_ *replace the final sentence with ...* -While it may be obvious, or even irrelevant, to you which error type pair_result could potentially have, the compiler has no way to know this. +While it may be obvious, or even irrelevant, to you which error type `pair_result` could potentially have, the compiler has no way to know this. Just like in other situations where the compiler cannot safely infer the exact type to use, you must annotate the block with a valid error type. We've already mentioned that `Result` automatically converts between error types where a suitable implementation of `Into` exists and you can leverage this and write: From 6f12e560167052e764b3c02e4497e90c1984be33 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 14 Apr 2026 09:46:35 +0000 Subject: [PATCH 07/24] update guide formatting --- text/0000-try-blocks-heterogeneous.md | 76 +++++++++++++-------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index c68456dc46d..ca540d0f914 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -175,45 +175,45 @@ _Assuming the explanation for try blocks is implemented as per RFC 3712, which c > | ^ > ``` > -> For now, the best solution for that mixed-error case is the same as before: to refactor it to a function. +> ~~For now, the best solution for that mixed-error case is the same as before: to refactor it to a function.~~ -*replace the final sentence with ...* +_replace the final sentence with ..._ -While it may be obvious, or even irrelevant, to you which error type `pair_result` could potentially have, the compiler has no way to know this. - -Just like in other situations where the compiler cannot safely infer the exact type to use, you must annotate the block with a valid error type. We've already mentioned that `Result` automatically converts between error types where a suitable implementation of `Into` exists and you can leverage this and write: - -```rust -let pair_result = try bikeshed Result<_, PairError> { - let a = std::fs::read_to_string("hello")?; - let b = std::fs::read_to_string("world")?; - let c: i32 = b.parse()?; - (a, c) -}; -``` - -As long as you have defined a suitable error: - -```rust -enum PairError { - IoError(Box), - ParseError(Box), -} - -impl From for PairError { - fn from(e: io::Error) -> Self { - Self::IoError(Box::new(e)) - } -} - -impl From for PairError { - fn from(e: num::ParseIntError) -> Self { - Self::ParseError(Box::new(e)) - } -} -``` - -Of course, there are crates available to simplify this if you do not want or need to create your own custom error type. +> While it may be obvious, or even irrelevant, to you which error type `pair_result` could potentially have, the compiler has no way to know this. +> +> Just like in other situations where the compiler cannot safely infer the exact type to use, you must annotate the block with a valid error type. We've already mentioned that `Result` automatically converts between error types where a suitable implementation of `Into` exists and you can leverage this and write: +> +> ```rust +> let pair_result = try bikeshed Result<_, PairError> { +> let a = std::fs::read_to_string("hello")?; +> let b = std::fs::read_to_string("world")?; +> let c: i32 = b.parse()?; +> (a, c) +> }; +> ``` +> +> As long as you have defined a suitable error: +> +> ```rust +> enum PairError { +> IoError(Box), +> ParseError(Box), +> } +> +> impl From for PairError { +> fn from(e: io::Error) -> Self { +> Self::IoError(Box::new(e)) +> } +> } +> +> impl From for PairError { +> fn from(e: num::ParseIntError) -> Self { +> Self::ParseError(Box::new(e)) +> } +> } +> ``` +> +> Of course, there are crates available to simplify this if you do not want or need to create your own custom error type. ## Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -280,8 +280,6 @@ fn heterogeneous_via_return_type() -> Result<(), Error1> { where the errors involved all implement `Into` - - Think about what the natural extension and evolution of your proposal would be and how it would affect the language and project as a whole in a holistic way. Try to use this section as a tool to more fully consider all possible From b4394ed2e09c92df40a95086c064237e575d63de Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 14 Apr 2026 11:49:19 +0000 Subject: [PATCH 08/24] Reference-level explanation --- text/0000-try-blocks-heterogeneous.md | 218 ++++++++++++++++++++++++++ 1 file changed, 218 insertions(+) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index ca540d0f914..8bc5638d684 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -218,6 +218,224 @@ _replace the final sentence with ..._ ## Reference-level explanation [reference-level-explanation]: #reference-level-explanation +This described the experimental implementation, currently in place in nightly, as implemented by [scottmcm in PR 149489](https://github.com/rust-lang/rust/pull/149489) with occasional additional comments by the RFC author. + +### Compiler changes + +#### Extend `ast::ExprKind::TryBlock` to store optional return type + +```rust +pub enum ExprKind { + ... + // previously: TryBlock(Box), + TryBlock(Box, Option>), + ... +} +``` + +with associated adjustments to `visit::walk_fn`, `assert!` + +#### Parse & pretty-print syntax `try bikeshed T {...}` + +1. Add (temporary) `bikeshed` keyword (see [unresolved-questions]) to available Tokens + + ```rust + enum TokenType { + ... + SymBikeshed, + ... + } + ``` + + ```rust + macro_rules! exp { + ... + (Bikeshed) => { exp!(@sym, bikeshed, SymBikeshed) }; + ... + } + ``` + +2. Add `bikeshed` & `try_blocks_heterogeneous` spanned symbols + + ```rust + symbols! { + ... + Symbols { + ... + bikeshed, + ... + try_blocks_heterogeneous, + } + ... + } + ``` + +3. Parse `try` blocks with optional `bikeshed` keyword + + ```rust + /// Parses a `try {...}` or `try bikeshed Ty {...}` expression (`try` token already eaten). + fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, Box> { + // ADD: do we have an annotated type? + let annotation = + if self.eat_keyword(exp!(Bikeshed)) { Some(self.parse_ty()?) } else { None }; + + let (attrs, body) = self.parse_inner_attrs_and_block(None)?; + if self.eat_keyword(exp!(Catch)) { + Err(self.dcx().create_err(errors::CatchAfterTry { span: self.prev_token.span })) + } else { + let span = span_lo.to(body.span); + //ADD: homogeneous & heterogeneous try blocks are behind separate feature gates + let gate_sym = + if annotation.is_none() { sym::try_blocks } else { sym::try_blocks_heterogeneous }; + + self.psess.gated_spans.gate(gate_sym, span); + Ok(self.mk_expr_with_attrs(span, ExprKind::TryBlock(body, annotation), attrs)) + } + } + ``` + + ```rust + fn is_try_block(&self) -> bool { + self.token.is_keyword(kw::Try) + && self.look_ahead(1, |t| { + *t == token::OpenBrace + || t.is_metavar_block() + // ADD: optional `bikeshed` following `try` + || t.kind == TokenKind::Ident(sym::bikeshed, IdentIsRaw::No) + }) + && self.token_uninterpolated_span().at_least_rust_2018() + } + ``` + +4. Correctly pretty print `bikeshed` annotated `try` blocks + + ```rust + ast::ExprKind::TryBlock(blk, opt_ty) => { + let cb = self.cbox(0); + let ib = self.ibox(0); + self.word_nbsp("try"); + // ADD: if there if a type annotation, prefix with `bikeshed` + if let Some(ty) = opt_ty { + self.word_nbsp("bikeshed"); + self.print_type(ty); + self.space(); + } + self.print_block_with_attrs(blk, attrs, cb, ib) + } + ``` + +#### Desugaring + +1. Introduce `TryBlockScope` enum + + ```rust + // The originating scope for an `Expr` when desugaring `?` + enum TryBlockScope { + /// There isn't a `try` block, so a `?` will use `return`. + Function, + /// We're inside a `try { … }` block, so a `?` will block-break + /// from that block using a type depending only on the argument. + Homogeneous(HirId), + /// We're inside a `try bikeshed _ { … }` block, so a `?` will block-break + /// from that block using the type specified. + Heterogeneous(HirId), + } + ``` + +2. Update desugaring `try` blocks at definition site + + ```rust + /// Desugar `try { ; }` into `{ ; ::std::ops::Try::from_output() }`, + /// `try { ; }` into `{ ; ::std::ops::Try::from_output(()) }` + /// and save the block id to use it as a break target for desugaring of the `?` operator. + fn lower_expr_try_block(&mut self, body: &Block, opt_ty: Option<&Ty>) -> hir::ExprKind<'hir> { + let body_hir_id = self.lower_node_id(body.id); + + // ADD differentiation + let new_scope = if opt_ty.is_some() { + TryBlockScope::Heterogeneous(body_hir_id) + } else { + TryBlockScope::Homogeneous(body_hir_id) + }; + let whole_block = self.with_try_block_scope(new_scope, |this| { + let mut block = this.lower_block_noalloc(body_hir_id, body, true); + ... + this.arena.alloc(block) + }); + + // ADD identification of `try bikeshed` as typed blocks + if let Some(ty) = opt_ty { + let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Path)); + let block_expr = self.arena.alloc(self.expr_block(whole_block)); + hir::ExprKind::Type(block_expr, ty) + } else { + hir::ExprKind::Block(whole_block, None) + } + } + ``` + +3. Update desugaring `?`, specifically in the construction of the `ControlFlow::Break` arm and the final return value + + ```rust + /// Desugar `ExprKind::Try` from: `?` into: + /// ```ignore (pseudo-rust) + /// match Try::branch() { + /// ControlFlow::Continue(val) => #[allow(unreachable_code)] val,, + /// ControlFlow::Break(residual) => + /// #[allow(unreachable_code)] + /// // If there is an enclosing `try {...}`: + /// break 'catch_target Residual::into_try_type(residual), + /// // Otherwise: + /// return Try::from_residual(residual), + /// } + /// ``` + fn lower_expr_try(&mut self, span: Span, sub_expr: &Expr) -> hir::ExprKind<'hir> { + ... + // `ControlFlow::Break(residual) => + // #[allow(unreachable_code)] + // return Try::from_residual(residual),` + let break_arm = { + ... + + // (hir::LangItem, Result) + let (constructor_item, target_id) = match self.try_block_scope { + TryBlockScope::Function => { + (hir::LangItem::TryTraitFromResidual, Err(hir::LoopIdError::OutsideLoopScope)) + } + TryBlockScope::Homogeneous(block_id) => { + (hir::LangItem::ResidualIntoTryType, Ok(block_id)) + } + // `try bikeshed` treated like a function for construction of residual expression, + // but with available Hirid for the source block + TryBlockScope::Heterogeneous(block_id) => { + (hir::LangItem::TryTraitFromResidual, Ok(block_id)) + } + }; + let from_residual_expr = self.wrap_in_try_constructor( + // replace previous inline `if let Some() else` to differentiate try block vs function + constructor_item, + try_span, + self.arena.alloc(residual_expr), + unstable_span, + ); + // replace `if let Some() else` with `if .is_ok() else` to differentiate try block vs function + let ret_expr = if target_id.is_ok() { + ... + // originating scope: try blocks vs function + match self.try_block_scope { + TryBlockScope::Homogeneous(block_id) | TryBlockScope::Heterogeneous(block_id) => { + hir::ExprKind::Break( + hir::Destination { label: None, target_id: Ok(block_id) }, + Some(from_yeet_expr), + ) + } + TryBlockScope::Function => self.checked_return(Some(from_yeet_expr)), + } + } + ``` + +(Note: also take this opportunity to rename various `catch...` items to `try...`) + This is the technical portion of the RFC. Explain the design in sufficient detail that: - Its interaction with other features is clear. From 844384568ba5e194e88cf9cc091a8cd363195e52 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 14 Apr 2026 11:49:43 +0000 Subject: [PATCH 09/24] tidy --- text/0000-try-blocks-heterogeneous.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 8bc5638d684..7aa86352fd6 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -434,16 +434,6 @@ with associated adjustments to `visit::walk_fn`, `assert!` } ``` -(Note: also take this opportunity to rename various `catch...` items to `try...`) - -This is the technical portion of the RFC. Explain the design in sufficient detail that: - -- Its interaction with other features is clear. -- It is reasonably clear how the feature would be implemented. -- Corner cases are dissected by example. - -The section should return to the examples given in the previous section, and explain more fully how the detailed proposal makes those examples work. - ## Drawbacks [drawbacks]: #drawbacks From 652a365ee69d15ecadc1665fdff25a9812918c18 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 14 Apr 2026 12:28:26 +0000 Subject: [PATCH 10/24] add annotated binding example --- text/0000-try-blocks-heterogeneous.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 7aa86352fd6..b261a6d3b26 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -473,7 +473,7 @@ Please also take into consideration that rust sometimes intentionally diverges f ## Future possibilities [future-possibilities]: #future-possibilities -### Allow inference via function return type +### Allow inference via function return type or variable binding For cases such as @@ -481,6 +481,7 @@ For cases such as fn heterogeneous_via_return_type() -> Result<(), Error1> { let x = try { err1("1")? + err2("2")? }?; let y = try { err2("1")? + err1("2")? }; + let _: Result<_, Error2> = try { err2("1")? + err1("2")? }; assert_eq!(x, y?); Ok(()) } From 444bf089cbc44ff634f234128dc92f1d4aa47c49 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 14 Apr 2026 12:29:14 +0000 Subject: [PATCH 11/24] drawbacks --- text/0000-try-blocks-heterogeneous.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index b261a6d3b26..16accde04e1 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -437,7 +437,7 @@ with associated adjustments to `visit::walk_fn`, `assert!` ## Drawbacks [drawbacks]: #drawbacks -Why should we _not_ do this? +This adds an additional keyword in a position and form which where there is not a single, well-ingrained idiom. While more complicated to implement it _may_ be possible to cover all required use cases via type inference, althought this could lead to a more complicated syntax for edge cases. ## Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From 84bd62eee1c461901bccdae8524e3dfddf12631c Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 14 Apr 2026 12:29:34 +0000 Subject: [PATCH 12/24] option to use no keyword --- text/0000-try-blocks-heterogeneous.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 16accde04e1..84b1319e9e0 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -467,7 +467,7 @@ Please also take into consideration that rust sometimes intentionally diverges f ## Unresolved questions [unresolved-questions]: #unresolved-questions -- [ ] What should the syntax be? See [Issue #154128](https://github.com/rust-lang/rust/issues/154128) for discussion of alternatives (`:`, `->`, `as`, ...) +- [ ] What should the syntax be? See [Issue #154128](https://github.com/rust-lang/rust/issues/154128) for discussion of alternatives (`:`, `->`, `as`, _nothing_ just `try T {}`, ...) - [ ] What type should be annotated? This should probably be the full type, with optional inferance, as currently implemented for `bikeshed`, but see [Issue #154127](https://github.com/rust-lang/rust/issues/154127) for discussion. ## Future possibilities From 94717e85675980d7c7ce1193731500c354f87fb3 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 14 Apr 2026 12:33:01 +0000 Subject: [PATCH 13/24] typos --- text/0000-try-blocks-heterogeneous.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 84b1319e9e0..46efa8ec0f6 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -90,7 +90,7 @@ fn heterogeneous_into_exists() { } ``` -The initial experimental approach to provide a prrof-of-concept introduced the (**deliberate placeholder**) syntax `try bikeshed ... {...}` in [PR #149489](https://github.com/rust-lang/rust/pull/149489). +The initial experimental approach to provide a proof-of-concept introduced the (**deliberate placeholder**) syntax `try bikeshed ... {...}` in [PR #149489](https://github.com/rust-lang/rust/pull/149489). >_For the remainder of this RFC we will continue with `bikeshed` to allow for examples which work on current nightly with `#![feature(try_blocks_heterogeneous)]`._ > @@ -468,7 +468,7 @@ Please also take into consideration that rust sometimes intentionally diverges f [unresolved-questions]: #unresolved-questions - [ ] What should the syntax be? See [Issue #154128](https://github.com/rust-lang/rust/issues/154128) for discussion of alternatives (`:`, `->`, `as`, _nothing_ just `try T {}`, ...) -- [ ] What type should be annotated? This should probably be the full type, with optional inferance, as currently implemented for `bikeshed`, but see [Issue #154127](https://github.com/rust-lang/rust/issues/154127) for discussion. +- [ ] What type should be annotated? This should probably be the full type, with optional inference, as currently implemented for `bikeshed`, but see [Issue #154127](https://github.com/rust-lang/rust/issues/154127) for discussion. ## Future possibilities [future-possibilities]: #future-possibilities From 6f5ab6c44f2e3411264bf561ca2c306b1ad3954c Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Thu, 16 Apr 2026 15:08:02 +0000 Subject: [PATCH 14/24] Drawbacks --- text/0000-try-blocks-heterogeneous.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 46efa8ec0f6..bf6bccfdc1e 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -437,7 +437,7 @@ with associated adjustments to `visit::walk_fn`, `assert!` ## Drawbacks [drawbacks]: #drawbacks -This adds an additional keyword in a position and form which where there is not a single, well-ingrained idiom. While more complicated to implement it _may_ be possible to cover all required use cases via type inference, althought this could lead to a more complicated syntax for edge cases. +This adds further syntax complexity to the language with another, slightly different, way in which types must be annotated. The open question on the correct syntax shows that whatever is chosen it will not be immediately obvious to users. ## Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From cea1ed0fb69e82b867840031217fc2da0d0db072 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Thu, 16 Apr 2026 15:15:57 +0000 Subject: [PATCH 15/24] add snowman --- text/0000-try-blocks-heterogeneous.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index bf6bccfdc1e..2a61b0ecd46 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -467,7 +467,7 @@ Please also take into consideration that rust sometimes intentionally diverges f ## Unresolved questions [unresolved-questions]: #unresolved-questions -- [ ] What should the syntax be? See [Issue #154128](https://github.com/rust-lang/rust/issues/154128) for discussion of alternatives (`:`, `->`, `as`, _nothing_ just `try T {}`, ...) +- [ ] What should the syntax be? See [Issue #154128](https://github.com/rust-lang/rust/issues/154128) for discussion of alternatives (`:`, `->`, `as`, _nothing_ just `try T {}`, or even `try ☃️ {...}` as in RFC3721) - [ ] What type should be annotated? This should probably be the full type, with optional inference, as currently implemented for `bikeshed`, but see [Issue #154127](https://github.com/rust-lang/rust/issues/154127) for discussion. ## Future possibilities From 4d9cd66c4952c83f21d457ae5ac6560be8be1994 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 21 Apr 2026 09:04:54 +0000 Subject: [PATCH 16/24] rationale & prior art --- text/0000-try-blocks-heterogeneous.md | 55 ++++++++++++++++++++------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 2a61b0ecd46..8cbed033d0a 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -442,27 +442,54 @@ This adds further syntax complexity to the language with another, slightly diffe ## Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -- Why is this design the best in the space of possible designs? -- What other designs have been considered and what is the rationale for not choosing them? -- What is the impact of not doing this? -- If this is a language proposal, could this be done in a library or macro instead? Does the proposed change make Rust code easier or harder to read, understand, and maintain? +### Homogenous try-blocks with manual error conversion + +Only support homogeneous `try` blocks and force manual conversion. + +For example, you could do something like + +```rust +try { + err1("1").map_err(Into::::into)? + + err2("2").map_err(Into::::into)? + }; +``` + +1. This leads much more verbose code where multiple error types are involved. +1. In cases where the final `Residual` is not any of the `Residuals` inside the `try` block (likely a very common situation with `anyhow`) this creates further verbosity by forcing turbofish annotation in at least one place. +1. Changing the block `Residual` requires multiple adjustments. +1. This breaks for cases where the `Try` type in question is not `Result`/`Option` unless it implements an equivalent of `map_err()`. +1. It is not immediately obvious to the user reading the block definition what the resulting `Residual` will be, the information is inside the block, not at the start of the definition. When you see `try bikeshed Foo {` you know the type without analysing the block. + +### Fold through some type function that attempts to merge residuals + +This is much less local, complex to implement and removes control from the user. + +### Could we evolve this in future? + +Once the correct keyword / syntax is identified the remainder is an early desugaring to existing features, this is the easiest kind of thing to change over editions. + +Therefore, if in the future we get new type system features that would allow improved "fallback hinting" or inference of unannotated +`try` blocks, we could relax the restrictions on homogeneous `try` blocks while still maintaining the ability to annotate for explicit +clarity or where inferance is not possible. This would be easy to achieve over an edition change, but we don't need to wait for an unknown to ship something now; we can switch how it works later easily enough. ## Prior art [prior-art]: #prior-art -Discuss prior art, both the good and the bad, in relation to this proposal. -A few examples of what this can include are: +Languages with traditional exceptions don't return a value from `try` blocks, so don't have this problem. +Even checked exceptions are still always the `Exception` type. -- For language, library, cargo, tools, and compiler proposals: Does this feature exist in other programming languages and what experience have their community had? -- For community proposals: Is this done by some other community and what were their experiences with it? -- For other teams: What lessons can we learn from what other communities have done here? -- Papers: Are there any published papers or great posts that discuss this? If you have some relevant papers to refer to, this can serve as a more detailed theoretical background. +In C#, the `?.` operator is scoped without a visible lexical block. -This section is intended to encourage you as an author to think about the lessons from other languages, provide readers of your RFC with a fuller picture. -If there is no prior art, that is fine - your ideas are interesting to us whether they are brand new or if it is an adaptation from other languages. +### Related RFCs & experimental features -Note that while precedent set by other languages is some motivation, it does not on its own motivate an RFC. -Please also take into consideration that rust sometimes intentionally diverges from common language features. +- [RFC2388 `try-expr`](https://rust-lang.github.io/rfcs/2388-try-expr.html) +- [RFC3721 `homogeneous_try_blocks`](https://rust-lang.github.io/rfcs/3721-homogeneous-try-blocks.html) +- [RFC3058 `try-trait-v2`](https://rust-lang.github.io/rfcs/3058-try-trait-v2.html) +- [Tracking issue #91285 `try_trait_v2_residual`](https://github.com/rust-lang/rust/issues/91285) +- [Tracking issue #63178 `Iterator::try_find`](https://github.com/rust-lang/rust/issues/63178) +- [Tracking issue #79711 `array::try_map`](https://github.com/rust-lang/rust/issues/79711) +- [Tracking issue #89379 `try_array_from_fn`](https://github.com/rust-lang/rust/issues/89379) ## Unresolved questions [unresolved-questions]: #unresolved-questions From 8d54ecf152593f7a5cbea5434fd2d12194b01993 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 21 Apr 2026 09:35:18 +0000 Subject: [PATCH 17/24] format related RFC list --- text/0000-try-blocks-heterogeneous.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 8cbed033d0a..4711882622d 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -483,9 +483,9 @@ In C#, the `?.` operator is scoped without a visible lexical block. ### Related RFCs & experimental features -- [RFC2388 `try-expr`](https://rust-lang.github.io/rfcs/2388-try-expr.html) -- [RFC3721 `homogeneous_try_blocks`](https://rust-lang.github.io/rfcs/3721-homogeneous-try-blocks.html) -- [RFC3058 `try-trait-v2`](https://rust-lang.github.io/rfcs/3058-try-trait-v2.html) +- [RFC #2388 `try-expr`](https://rust-lang.github.io/rfcs/2388-try-expr.html) +- [RFC #3721 `homogeneous_try_blocks`](https://rust-lang.github.io/rfcs/3721-homogeneous-try-blocks.html) +- [RFC #3058 `try-trait-v2`](https://rust-lang.github.io/rfcs/3058-try-trait-v2.html) - [Tracking issue #91285 `try_trait_v2_residual`](https://github.com/rust-lang/rust/issues/91285) - [Tracking issue #63178 `Iterator::try_find`](https://github.com/rust-lang/rust/issues/63178) - [Tracking issue #79711 `array::try_map`](https://github.com/rust-lang/rust/issues/79711) From 84290225410c9ae595a8251e938e53c2d911e5ce Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 21 Apr 2026 09:46:50 +0000 Subject: [PATCH 18/24] spell check --- text/0000-try-blocks-heterogeneous.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 4711882622d..fb27d9c5dc7 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -406,7 +406,7 @@ with associated adjustments to `visit::walk_fn`, `assert!` (hir::LangItem::ResidualIntoTryType, Ok(block_id)) } // `try bikeshed` treated like a function for construction of residual expression, - // but with available Hirid for the source block + // but with available HirId for the source block TryBlockScope::Heterogeneous(block_id) => { (hir::LangItem::TryTraitFromResidual, Ok(block_id)) } @@ -471,7 +471,7 @@ Once the correct keyword / syntax is identified the remainder is an early desuga Therefore, if in the future we get new type system features that would allow improved "fallback hinting" or inference of unannotated `try` blocks, we could relax the restrictions on homogeneous `try` blocks while still maintaining the ability to annotate for explicit -clarity or where inferance is not possible. This would be easy to achieve over an edition change, but we don't need to wait for an unknown to ship something now; we can switch how it works later easily enough. +clarity or where inference is not possible. This would be easy to achieve over an edition change, but we don't need to wait for an unknown to ship something now; we can switch how it works later easily enough. ## Prior art [prior-art]: #prior-art From eac3764734e7d009fbd714a8b14cd2bbe2c93fb2 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 21 Apr 2026 09:55:44 +0000 Subject: [PATCH 19/24] fix broken quoted link --- text/0000-try-blocks-heterogeneous.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index fb27d9c5dc7..f6d552b2f5c 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -143,7 +143,7 @@ _Assuming the explanation for try blocks is implemented as per RFC 3712, which c > will do the right thing with minimal syntactic overhead. It's also common to want > to group a bunch of things with the same error type. Perhaps it's a bunch of calls > to one library, which all use that library's error type. Or you want to do -> [a bunch of `io` operations](> > compiler/rustc_borrowck/src/nll.rs#L355-L367) which all use `io::Result`. Additionally, `try` blocks work with +> [a bunch of `io` operations](https://github.com/rust-lang/rust/blob/d6f3a4ecb48ead838638e902f2fa4e5f3059779b/compiler/rustc_borrowck/src/nll.rs#L355-L367) which all use `io::Result`. Additionally, `try` blocks work with > `?`-on-`Option` as well, where error-conversion is never needed, since there is only `None`. > > It will fail to compile, however, if not everything shares the same error type. From d3ea798189e467ec21dc1ea3619087f1acd93d33 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 21 Apr 2026 09:56:51 +0000 Subject: [PATCH 20/24] typos --- text/0000-try-blocks-heterogeneous.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index f6d552b2f5c..03a72088b92 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -314,7 +314,7 @@ with associated adjustments to `visit::walk_fn`, `assert!` let cb = self.cbox(0); let ib = self.ibox(0); self.word_nbsp("try"); - // ADD: if there if a type annotation, prefix with `bikeshed` + // ADD: if there is a type annotation, prefix with `bikeshed` if let Some(ty) = opt_ty { self.word_nbsp("bikeshed"); self.print_type(ty); @@ -442,7 +442,7 @@ This adds further syntax complexity to the language with another, slightly diffe ## Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -### Homogenous try-blocks with manual error conversion +### Homogeneous try-blocks with manual error conversion Only support homogeneous `try` blocks and force manual conversion. @@ -455,7 +455,7 @@ try { }; ``` -1. This leads much more verbose code where multiple error types are involved. +1. This leads to much more verbose code where multiple error types are involved. 1. In cases where the final `Residual` is not any of the `Residuals` inside the `try` block (likely a very common situation with `anyhow`) this creates further verbosity by forcing turbofish annotation in at least one place. 1. Changing the block `Residual` requires multiple adjustments. 1. This breaks for cases where the `Try` type in question is not `Result`/`Option` unless it implements an equivalent of `map_err()`. From f3048887e52841311a6ff13a7ea5510c6a87616a Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 21 Apr 2026 09:58:31 +0000 Subject: [PATCH 21/24] add reference to yeet --- text/0000-try-blocks-heterogeneous.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 03a72088b92..10d2ae9b202 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -490,6 +490,7 @@ In C#, the `?.` operator is scoped without a visible lexical block. - [Tracking issue #63178 `Iterator::try_find`](https://github.com/rust-lang/rust/issues/63178) - [Tracking issue #79711 `array::try_map`](https://github.com/rust-lang/rust/issues/79711) - [Tracking issue #89379 `try_array_from_fn`](https://github.com/rust-lang/rust/issues/89379) +- [Tracking issue #96373 `yeet_expr`](https://github.com/rust-lang/rust/issues/96373) ## Unresolved questions [unresolved-questions]: #unresolved-questions From 9701bd4555cf46c2316d9f534023aea6549ea000 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 21 Apr 2026 09:59:52 +0000 Subject: [PATCH 22/24] number switch --- text/0000-try-blocks-heterogeneous.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 10d2ae9b202..662cea57265 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -136,7 +136,7 @@ fn heterogeneous_into_anyhow() { ## Guide-level explanation [guide-level-explanation]: #guide-level-explanation -_Assuming the explanation for try blocks is implemented as per RFC 3712, which contains:_ +_Assuming the explanation for try blocks is implemented as per RFC 3721, which contains:_ > This behaviour is what you want in the vast majority of simple cases. In particular, > it always works for things with just one `?`, so simple things like `try { a? + 1 }` From 80ad9a31101ea47b2ee02dce24195bf19b7b9f21 Mon Sep 17 00:00:00 2001 From: Mike Foster Date: Tue, 21 Apr 2026 10:06:20 +0000 Subject: [PATCH 23/24] typo in example --- text/0000-try-blocks-heterogeneous.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index 662cea57265..bf195b0ebe2 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -64,7 +64,7 @@ impl From for Error2 { } impl From for Error2 { - fn from(_: Error2) -> Self { + fn from(_: Error1) -> Self { Self } } From 0798663debc67bf866b02d805e1c449136fa652a Mon Sep 17 00:00:00 2001 From: MusicalNinjaDad <102677655+MusicalNinjaDad@users.noreply.github.com> Date: Tue, 21 Apr 2026 12:45:14 +0200 Subject: [PATCH 24/24] Remove remaining placeholder text thanks @kennytm Co-authored-by: kennytm --- text/0000-try-blocks-heterogeneous.md | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/text/0000-try-blocks-heterogeneous.md b/text/0000-try-blocks-heterogeneous.md index bf195b0ebe2..75899cdff18 100644 --- a/text/0000-try-blocks-heterogeneous.md +++ b/text/0000-try-blocks-heterogeneous.md @@ -517,20 +517,3 @@ fn heterogeneous_via_return_type() -> Result<(), Error1> { where the errors involved all implement `Into` -Think about what the natural extension and evolution of your proposal would -be and how it would affect the language and project as a whole in a holistic -way. Try to use this section as a tool to more fully consider all possible -interactions with the project and language in your proposal. -Also consider how this all fits into the roadmap for the project -and of the relevant sub-team. - -This is also a good place to "dump ideas", if they are out of scope for the -RFC you are writing but otherwise related. - -If you have tried and cannot think of any future possibilities, -you may simply state that you cannot think of anything. - -Note that having something written down in the future-possibilities section -is not a reason to accept the current or a future RFC; such notes should be -in the section on motivation or rationale in this or subsequent RFCs. -The section merely provides additional information.