RustArena

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

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

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 `()`

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