DaleSchool

A Taste of Error Handling: panic! and Result

Intermediate15min

Learning Objectives

  • Explain the difference between panic! and Result
  • Understand what unwrap() does
  • Know why the unwrap() calls from Phase 1 can be dangerous

Working Code

In Lesson 07 you used unwrap() in the number-guessing game. Back then, the explanation was just "if there's an error, stop the program." Now it's time to understand what that really means.

fn main() {
    // Scenario: reading a number from input
    let input = "42";
    let number: i32 = input.parse().unwrap();
    println!("Number: {number}");

    let bad_input = "abc";
    let result: Result<i32, _> = bad_input.parse();
    println!("Result: {:?}", result);
}

Run it! "42" converts fine, but "abc" gives you Err(...). If you'd written bad_input.parse().unwrap(), the program would have crashed.

Try It Yourself

  1. Try running bad_input.parse().unwrap() directly. What error message do you get?
  2. Change input to "99999999999999999999". What happens when it overflows the i32 range?

"Why?" — Two Kinds of Errors

Rust divides errors into two categories:

| | Unrecoverable (panic!) | Recoverable (Result) | | -------- | ------------------------------------------------ | ------------------------------------------ | | Analogy | Building fire — evacuate! | Failed delivery — retry | | When | Programming bugs, should-never-happen situations | File not found, network disconnected, etc. | | Handling | Program terminates immediately | Receive the error and respond |

panic! — Unrecoverable Errors

panic! means "we can't continue — shut everything down."

fn main() {
    println!("Starting!");
    panic!("Something went horribly wrong!");
    println!("This line never runs");
}

Out-of-bounds indexing also triggers a panic. You saw this in Lesson 06:

fn main() {
    let v = vec![1, 2, 3];
    println!("{}", v[10]); // panic!
}

Result — Like Checking a Delivery Status

Result is an enum that holds either success or failure. The enums from Lesson 13 make another appearance!

enum Result<T, E> {
    Ok(T),    // Success! Contains the value
    Err(E),   // Failure! Contains error info
}

Think of it like checking a package delivery:

  • Ok(value) — Delivered! Here's your item
  • Err(error) — Delivery failed! Here's the reason
fn divide(a: f64, b: f64) -> Result<f64, String> {
    if b == 0.0 {
        Err(String::from("Cannot divide by zero"))
    } else {
        Ok(a / b)
    }
}

fn main() {
    let result1 = divide(10.0, 3.0);
    let result2 = divide(10.0, 0.0);

    println!("10 / 3 = {:?}", result1);
    println!("10 / 0 = {:?}", result2);
}

Handling Result With match

You can handle Result with the match you learned in Lesson 13:

fn main() {
    let input = "42";
    let result: Result<i32, _> = input.parse();

    match result {
        Ok(number) => println!("Conversion succeeded: {number}"),
        Err(e) => println!("Conversion failed: {e}"),
    }
}

This is exactly the pattern from the number-guessing game in Lesson 07:

let guess: i32 = match guess.trim().parse() {
    Ok(num) => num,
    Err(_) => {
        println!("Please enter a number!");
        continue;
    }
};

Back then you didn't know match well, but now you can see it's handling Result's Ok and Err variants.

unwrap() — Convenient but Dangerous

unwrap() is the simplest way to extract a value from a Result. But it panics on Err:

fn main() {
    let ok_result: Result<i32, String> = Ok(42);
    let value = ok_result.unwrap(); // 42
    println!("{value}");

    // let err_result: Result<i32, String> =
    //     Err(String::from("error!"));
    // err_result.unwrap(); // panic!
}

Here's what unwrap() does:

  • Ok(value) -> extracts the value
  • Err(error) -> panics and crashes the program

In Lesson 07 you wrote io::stdin().read_line(&mut guess).unwrap(). If reading input failed, the program would just die. Fine for practice code, but dangerous in real programs.

Learn More: expect() — A Better unwrap()

expect() works like unwrap() but lets you specify a custom error message:

fn main() {
    let input = "abc";
    let number: i32 = input
        .parse()
        .expect("Failed to convert to number");
    // panic message: "Failed to convert to number: ..."
}

It's easier to find where an error occurred, making expect() better than unwrap(). But both still cause a panic.

You'll learn the ? operator for elegantly propagating errors in Phase 3!

Exercise 1. Complete the function to parse a string into i32. Return 0 as the default if parsing fails.

fn parse_or_default(s: &str) -> i32 {
    // Complete this
    // Hint: use match on s.parse::<i32>()
}

fn main() {
    println!("{}", parse_or_default("42"));   // 42
    println!("{}", parse_or_default("abc"));  // 0
    println!("{}", parse_or_default(""));     // 0
}

Exercise 2. Complete the find_item function. Return Ok(index) if found, or Err(message) if not.

fn find_item(items: &[&str], target: &str) -> Result<usize, String> {
    // Complete this
    // Hint: use for with enumerate()
}

fn main() {
    let fruits = vec!["apple", "banana", "grape"];

    match find_item(&fruits, "banana") {
        Ok(idx) => println!("banana is at index {idx}!"),
        Err(msg) => println!("{msg}"),
    }

    match find_item(&fruits, "strawberry") {
        Ok(idx) => println!("strawberry is at index {idx}!"),
        Err(msg) => println!("{msg}"),
    }
}

Q1. What is the biggest difference between panic! and Result?

  • A) panic! is slow and Result is fast
  • B) panic! immediately stops the program; Result returns the error as a value
  • C) panic! handles numbers only; Result handles strings only
  • D) No difference

Q2. Which statement about unwrap() is correct?

  • A) It always returns None
  • B) On Ok it extracts the value; on Err it triggers a panic
  • C) It ignores errors and returns an empty value
  • D) It prints the error and continues execution

Q3. What does this code print?

fn main() {
    let result: Result<i32, &str> = Err("failure");
    let value = match result {
        Ok(n) => n,
        Err(_) => -1,
    };
    println!("{value}");
}
  • A) "failure"
  • B) -1
  • C) 0
  • D) The program panics