I heard the try {}
block syntax was now available experimentally in Nightly. I am very excited about this. The main reason I am excited is I expect it to mitigate frustrations where Rust does not currently allow mixing ?
types (Option, Result, different types of Result) within a single function body.
I do notice one thing, which is that there is no way for a try body to explicitly declare its return type. Consider this example, which I tested on rustc 1.81.0-nightly (c1b336cb6 2024-06-21)
:
#![feature(try_blocks)]
fn main() {
let x = try {
let a : std::io::Result<()> = Ok(());
let b : std::fmt::Result = Ok(());
let c = a?;
let d = b?;
};
println!("Hello, world!");
}
This fails with the error:
error[E0282]: type annotations needed
--> src/main.rs:4:9
|
4 | let x = try {
| ^
|
help: consider giving `x` an explicit type
|
4 | let x: /* Type */ = try {
| ++++++++++++
For more information about this error, try `rustc --explain E0282`.
error: could not compile `deletemetry` (bin "deletemetry") due to 1 previous error
I would like to propose that a <type>
be allowed on the try {}
block, like:
#![feature(try_blocks)]
fn main() {
let x = try<std::io::Result<()> {
let a : std::io::Result<()> = Ok(());
let b : std::fmt::Result = Ok(());
let c = a?;
let d = b?;
};
println!("Hello, world!");
}
At present, this fails with
Compiling deletemetry v0.1.0 (/home/mcc/work/temp/deletemetry)
error: expected expression, found reserved keyword `try`
--> src/main.rs:4:13
|
4 | let x = try<std::io::Result<()> {
| ^^^ expected expression
error: could not compile `deletemetry` (bin "deletemetry") due to 1 previous error
I think this would be consistent with other typed structures. The main benefit I foresee is better error messages. I anticipate people would use this explicit-try-type feature relatively rarely, but in unusual situations and when inference is making bad guesses it would be helpful.
Forseeing potential objections:
Why not just type the variable being assigned to, as the error help suggests?
This example is designed to be minimal. I can imagine scenarios where typing the assignment is not an option, such as passing the result of a try {} to a function, returning it from a closure, or using try {}.into()
.
Why not just get better inference?
In principle, the Rust compiler could have inferred a type for the try {}
expression, for example guessing the first ?
type, std::io::Result<()>
, as the correct one. (Right now there seems to be no inference on try {}
s at all; if I comment out the let b
and let d
statements above I currently get the same error.) However, inference will inherently struggle in the case where the code itself is wrong (as my sample code above indeed does seem to be). Allowing me as the programmer to explicitly state my expected return value for the try {}
block will give me the most useful error messages in a situation where I have given the compiler gibberish and therefore it is inferring the wrong type.
For example, in the case of my second example block above, my "expected" result is that I would get an error message on the line let d = b?;
, as this is the line which violates the stated return value. This would have been a more useful error message than the try
block as a whole simply telling me "I can't figure out what type you wanted here".
Why not use the syntax try -> std::io::Result<()>
?
I have no strong opinion on the exact syntax for this feature, I just want a way to do it. (In testing I did try try -> std::io::Result<()>
and try : std::io::Result<()>
in case there was already a syntax for this and I'd guessed it wrong, but these were not recognized by the compiler.)
Am I posting this in the correct place?
I initially attempted to file this as a bug on Github but it said feature requests should go here. I think there is a tracking issue for this feature but it seems to be kind of a forest of sub-tracking-issues, so I do not know how to check if the feature I am requesting here is already planned.