Step 5 of 7
main can return a Result
? needs a function that returns Result, and main is a function. It can return Result<(), E>:
use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError> {
let width: u32 = "1920".parse()?;
let height: u32 = "1080".parse()?;
println!("{} pixels", width * height);
Ok(())
}
2073600 pixels
Result<(), ParseIntError>: on success there's nothing to give back, so theOktype is(), the unit type.Ok(())at the end says "finished successfully". The double parentheses areOkwrapped around the unit value().- If any
?hits an error,mainreturns thatErr.
What happens when main returns Err
Rust prints Error: followed by the error's {:?} form to stderr, and the program ends with exit code 1 instead of 0. The exit code is how a program tells whoever started it (a shell script, a CI pipeline, another program) whether it worked: 0 means success, anything else means failure.
$ cargo run
Error: ParseIntError { kind: InvalidDigit }
$ echo $?
1
This makes main() -> Result great for tools and scripts: you write the happy path with ? and every failure stops the program with a non-zero exit code. The message is the developer-oriented {:?} form, though. Polished command-line tools print a friendly message themselves (module 40 covers that).
Any error type works, as long as it can be printed with {:?}. With String errors:
fn main() -> Result<(), String> {
Err(String::from("config file missing"))
}
// prints to stderr: Error: "config file missing"
Common mistakes
- Forgetting
Ok(())at the end. Without it,main's body ends with a statement, so it "returns"(), not aResult:
use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError> {
let width: u32 = "1920".parse()?;
println!("{width}");
}
error[E0308]: mismatched types
| fn main() -> Result<(), ParseIntError> {
| ---- ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Result<(), ParseIntError>`, found `()`
- Mixing error types in
main.read_linefails with anstd::io::Error,parsewith aParseIntError. Amainthat returnsResult<(), ParseIntError>can't use?on both. The next module introducesBox<dyn Error>, a type that holds any error, which solves this. Until then,expectonread_lineis reasonable: if reading stdin fails, there's nothing better to do.
Your task
A scoreboard reads points from input (several per line, separated by spaces) and prints the running total after each one. Make main return Result<(), ParseIntError> and replace the unwrap() with ?, so a bad value stops the program with exit code 1 (after the totals printed so far). For input 10 20 30 the output is:
running total: 10
running total: 30
running total: 60
Challenge 2
A room planner reads one line with a width and a height. Make main return Result<(), ParseIntError> and use ? for both parses. Print width: <w> right after parsing the width, then height: <h>, then area: <w*h>. For input 3 4:
width: 3
height: 4
area: 12
If a value is missing or not a number, the program should stop with exit code 1.
Challenge 3
An ATM processes three withdrawals, 30, 50 and 40, from a balance of 100. Make main return Result<(), String> and apply each withdrawal with withdraw(...)?, printing withdrew <amount>, balance <balance> after each success and all done at the end.
The third withdrawal fails, so your program should print two lines and then stop with exit code 1 (Rust prints the Error: ... line to stderr).
Previous: The ? operator Next: Combinators: map, and_then, ok_or, map_err