Basic Error Handling

Back

Loading concept...

šŸ›”ļø Rust Error Handling: Your Safety Net for Code

The Story of the Brave Little Program

Imagine you’re building a tower with blocks. Sometimes a block is missing, sometimes it’s the wrong shape. What do you do? You don’t just let the tower fall! You handle the problem.

That’s exactly what error handling in Rust is about. Rust is like a careful builder who always has a plan when something goes wrong.


šŸŽÆ The Five Safety Tools

Think of error handling in Rust like having five different safety tools in your toolbox:

Tool What It Does When to Use It
panic! Stops everything immediately ā€œThe house is on fire!ā€
unwrap Gets the value or crashes ā€œI’m 100% sure this worksā€
expect Like unwrap, but with a message ā€œI’m sure, but just in caseā€¦ā€
Result Asks ā€œDid it work or not?ā€ ā€œLet’s check carefullyā€
Combinators Chain multiple checks together ā€œDo this, then this, then thisā€

šŸ”“ 1. The panic! Macro - The Emergency Stop Button

What is it?

panic! is like pulling the fire alarm. It immediately stops your program and says ā€œSomething went REALLY wrong!ā€

Simple Example

fn main() {
    panic!("Oh no! Something terrible happened!");
}

What happens: Your program stops right there. It prints an error message and exits.

When to Use It?

Use panic! only when:

  • Something impossible happened
  • There’s no way to recover
  • It’s a bug in your code
fn divide(a: i32, b: i32) -> i32 {
    if b == 0 {
        panic!("Cannot divide by zero!");
    }
    a / b
}

šŸ’” Think of it like:

ā€œIf the car has no wheels, don’t try to drive it. Just stop!ā€


🟔 2. The unwrap Method - The Confident Friend

What is it?

unwrap says: ā€œI KNOW there’s a value here. Just give it to me!ā€

But if there’s NO value… your program crashes.

The Two Boxes

In Rust, some things come in special boxes:

  • Some(value) = ā€œHere’s your prize!ā€
  • None = ā€œThe box is emptyā€
fn main() {
    let gift = Some(42);

    // unwrap opens the box
    let number = gift.unwrap();
    println!("I got: {}", number);
}

Output: I got: 42

āš ļø The Danger

fn main() {
    let empty_box: Option<i32> = None;

    // This will CRASH!
    let number = empty_box.unwrap();
}

What happens: PANIC! The program crashes because you tried to unwrap an empty box.

šŸ’” Think of it like:

ā€œOpening a present without checking if there’s actually something inside.ā€


🟢 3. The expect Method - The Prepared Friend

What is it?

expect is just like unwrap, but it lets you leave a note about why this is important.

Example

fn main() {
    let config = Some("settings.txt");

    // With expect, you explain WHY this matters
    let filename = config.expect(
        "Config file is required to start!"
    );

    println!("Using: {}", filename);
}

Why is expect Better Than unwrap?

When something goes wrong, expect tells you what went wrong:

// This crashes with a helpful message
let empty: Option<i32> = None;
let value = empty.expect("We need a value here!");

Output: We need a value here!

vs just unwrap:

// This crashes with a confusing message
let empty: Option<i32> = None;
let value = empty.unwrap();

Output: called Option::unwrap() on a None value

šŸ’” Think of it like:

ā€œLeaving a sticky note on the present: ā€˜This MUST contain birthday money from grandma!ā€™ā€


šŸ”µ 4. The Result Type - The Smart Question Asker

What is it?

Result is like asking: ā€œDid it work, or did something go wrong?ā€

It has two answers:

  • Ok(value) = ā€œSuccess! Here’s what you wantedā€
  • Err(error) = ā€œOops! Here’s what went wrongā€

Simple Diagram

graph TD A["Try Something"] --> B{Did it work?} B -->|Yes| C["Ok - Here&&#35;39;s your value!] B --&gt;&#124;No&#124; D[Err - Here&&#35;39;s what went wrong"]

Real Example: Reading a File

use std::fs::File;

fn main() {
    let result = File::open("hello.txt");

    match result {
        Ok(file) => {
            println!("File opened!");
        }
        Err(error) => {
            println!("Problem: {}", error);
        }
    }
}

Creating Your Own Result

fn divide(a: i32, b: i32) -> Result<i32, String> {
    if b == 0 {
        Err(String::from("Cannot divide by zero!"))
    } else {
        Ok(a / b)
    }
}

fn main() {
    match divide(10, 2) {
        Ok(answer) => println!("Answer: {}", answer),
        Err(e) => println!("Error: {}", e),
    }
}

Output: Answer: 5

šŸ’” Think of it like:

ā€œAsking ā€˜Did you find my toy?’ and getting either ā€˜Yes, here it is!’ or ā€˜No, but I looked under the bedā€™ā€


🟣 5. Result Combinators - The Chain of Helpers

What are Combinators?

Combinators are helper methods that let you chain operations together without writing lots of match statements.

The Main Combinators

Combinator What It Does
map Transform the success value
map_err Transform the error
and_then Chain another operation
unwrap_or Get value or use default
unwrap_or_else Get value or run a function
? operator Return early if error

šŸ”§ map - Transform Success

fn main() {
    let number: Result<i32, &str> = Ok(5);

    // Double the number if successful
    let doubled = number.map(|n| n * 2);

    println!("{:?}", doubled); // Ok(10)
}

šŸ”§ and_then - Chain Operations

fn parse_and_double(s: &str) -> Result<i32, String> {
    s.parse::<i32>()
        .map_err(|_| String::from("Not a number!"))
        .and_then(|n| {
            if n < 0 {
                Err(String::from("No negatives!"))
            } else {
                Ok(n * 2)
            }
        })
}

fn main() {
    println!("{:?}", parse_and_double("5"));  // Ok(10)
    println!("{:?}", parse_and_double("-3")); // Err
}

šŸ”§ unwrap_or - Default Values

fn main() {
    let good: Result<i32, &str> = Ok(42);
    let bad: Result<i32, &str> = Err("oops");

    println!("{}", good.unwrap_or(0)); // 42
    println!("{}", bad.unwrap_or(0));  // 0
}

šŸ”§ The ? Operator - The Magic Shortcut

The ? operator is like saying: ā€œIf this fails, just return the error. Don’t bother continuing.ā€

Without ?:

fn read_username() -> Result<String, io::Error> {
    let file = match File::open("user.txt") {
        Ok(f) => f,
        Err(e) => return Err(e),
    };
    // ... more code
}

With ?:

fn read_username() -> Result<String, io::Error> {
    let file = File::open("user.txt")?;
    // ... more code
}

The ? does the same thing but in one character!

šŸ’” Think of it like:

ā€œIf the door is locked, just go home. Don’t stand there forever.ā€


šŸŽØ Visual Summary

graph TD A["Something might fail"] --> B{How critical?} B -->|Impossible to continue| C["panic!"] B -->|I'm certain it works| D["unwrap"] B -->|Certain, want message| E["expect"] B -->|Need to handle gracefully| F["Result"] F --> G["Use combinators to chain"] G --> H["map, and_then, unwrap_or"] G --> I["? operator for clean code"]

šŸš€ When to Use What?

Situation Best Choice
ā€œThe program can’t possibly continueā€ panic!
ā€œI’m 100% sure this won’t failā€ unwrap
ā€œI’m sure, but want a clear errorā€ expect
ā€œThis might fail, handle itā€ Result
ā€œChain multiple fallible operationsā€ Combinators + ?

šŸ’Ŗ You’ve Got This!

Remember: Errors are not scary. They’re just messages saying ā€œHey, something needs your attention!ā€

Rust’s error handling makes your programs safe and predictable. Now you have five powerful tools to handle any situation:

  1. šŸ”“ panic! - Emergency stop
  2. 🟔 unwrap - Confident extraction
  3. 🟢 expect - Annotated confidence
  4. šŸ”µ Result - Graceful handling
  5. 🟣 Combinators - Elegant chaining

Go forth and write code that handles errors like a pro! šŸ¦€āœØ

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.