While looking through the RFC that @zackw posted, I kind of feel like it’s side-stepping a huge issue, which is what the regular “non-failure” return value needs to be. Look at this example from the RFC:
fn main() -> Result<(), io::Error> {
let mut stdin = io::stdin();
let mut raw_stdout = io::stdout();
let mut stdout = raw_stdout.lock();
for line in stdin.lock().lines() {
stdout.write(line?.trim().as_bytes())?;
stdout.write(b"\n")?;
}
stdout.flush()
}
This looks great, but it only looks great because the last operation happens to be the stdout.flush()
. Suppose that we wanted to do something else after:
fn main() -> Result<(), io::Error> {
let mut stdin = io::stdin();
let mut raw_stdout = io::stdout();
let mut stdout = raw_stdout.lock();
for line in stdin.lock().lines() {
stdout.write(line?.trim().as_bytes())?;
stdout.write(b"\n")?;
}
stdout.flush()?;
some_method_call();
Ok(())
}
Here we need to add that kind of ugly Ok(())
at the end of main. It’s not particularly intuitive why this needs to be there for someone new to Rust, and it’s also really subtle that it becomes unnecessary in the first example due to the omitted semicolon. Ideally this would “just work” without having to return anything special from main
.
It might be worth considering if something like the original “checked exception” proposal isn’t more sensible. I think it was @glaebhoerl that first proposed this somewhere. Of course this is nothing like checked exceptions in Java, instead it’s just a little bit more syntax sugar on top of what we already have. The example above would become:
fn main() throws io::Error {
let mut stdin = io::stdin();
let mut raw_stdout = io::stdout();
let mut stdout = raw_stdout.lock();
for line in stdin.lock().lines() {
stdout.write(line?.trim().as_bytes())?;
stdout.write(b"\n")?;
}
stdout.flush()?;
some_method_call();
}
With the exact same semantics. That is the return value of main
is still Result<(), io::Error>
, just like before. However, the value that the function body evaluates to does not need to be (and perhaps cannot be) wrapped in a Result
.
Together with some Trait
and adding std::error::Error
to the prelude this could become:
fn main() throws some Error {
let mut stdin = io::stdin();
let mut raw_stdout = io::stdout();
let mut stdout = raw_stdout.lock();
for line in stdin.lock().lines() {
stdout.write(line?.trim().as_bytes())?;
stdout.write(b"\n")?;
}
stdout.flush()?;
some_method_call();
}
That looks pretty nice and self-explanatory to me.
Perhaps this could be considered a completely orthogonal issue, but I thought I’d bring it up here anyway.