I actually really like this proposal. Let’s play a game! Try to figure out what the next function/block returns!
fn foo() -> i32 catch Error { ... }
fn bar() -> i32 catch as Option { ... }
fn baz() -> i32 catch ! as Future { ... }
catch Error { ... }
catch as Option { ... }
catch ! as Future { ... }
Could you guess all 6? If you need help, here is the insides!
{
let val = may_fail()?;
val + 1
}
This syntax is very ergonomic to read and write, while no information is hidden. The return values are obvious to the caller just from the signature (Result<i32,Error>
, Option<i32>
, Future<i32,!>
) while allowing to use the same tools for everything that implements Try
.
The proposal could benefit from slightly changing the following:
fn foo() -> i32 catches as Option {
let x = maybe_value()?;
if x < 3 {
throw;
}
if x > 4 {
return x-1;
}
x
}
Specifying the error type is optional, but required if the compiler can’t figure out the type just from the signature:
//The following functions are exactly the same!
fn foo() -> i32 catch io::Error { 3 }
fn foo() -> i32 catch io::Error as Result { 3 }
fn foo() -> i32 catch as Result<_,io::Error> { 3 }
fn foo() -> i32 catch as io::Result { 3 }
catch
tells the compiler that the function returns something that implements Try
, and is assumed to be Result
unless specified otherwise with the keyword as
.
return
and throw
are taking the inner value, with similar syntax to if there wasn’t error handling. If the inner value is of type (), you just type return;
or throw;
It is clear what the function returns because “It’s right there in the signature, people!”.
Edit: This syntax looks better when throws
is used:
fn foo() -> i32 throws io::Error { 3 }
fn foo() -> i32 throws as io::Result { 3 }
fn foo() throws as Option {}
Edit 2: Let’s refactor some code! Here is a huge function and it’s use:
fn huge() {
// OMG I'm so huge
}
huge();
Let’s allow this function to possibly fail:
fn huge() throws as Option {
and we need to handle the error
huge()?;
Done.
We just changed 2 lines, and the function compiles once again and does exactly what it did before, no matter what the insides were, but we can add cases where the function will fail inside of it.