Module 3 • Lesson 18

Option and Result Types

📚 9 min read 💻 Free Course 🦀 nixus.pro

Option<T>: Null Safety

fn find_index(haystack: &[i32], needle: i32) -> Option {
    haystack.iter().position(|&x| x == needle)
}

fn safe_divide(a: f64, b: f64) -> Option {
    if b == 0.0 { None } else { Some(a / b) }
}

fn main() {
    let v = vec![10, 20, 30, 40, 50];

    // match
    match find_index(&v, 30) {
        Some(i) => println!("Found at {}", i),
        None    => println!("Not found"),
    }

    // if let (cleaner for single case)
    if let Some(i) = find_index(&v, 50) {
        println!("50 is at index {}", i);
    }

    // unwrap_or, map, and_then
    let result = safe_divide(10.0, 0.0).unwrap_or(f64::INFINITY);
    println!("{}", result); // inf

    let doubled = safe_divide(10.0, 2.0)
        .map(|x| x * 2.0)
        .filter(|&x| x > 5.0)
        .unwrap_or(0.0);
    println!("{}", doubled); // 10.0

    // ? operator in Option-returning functions
    fn first_doubled(v: &[i32]) -> Option {
        Some(v.first()? * 2)
    }
    println!("{:?}", first_doubled(&[5, 10])); // Some(10)
    println!("{:?}", first_doubled(&[]));       // None
}

Result<T, E>: Recoverable Errors

use std::num::ParseIntError;

fn parse_positive(s: &str) -> Result {
    let n: i32 = s.trim().parse()
        .map_err(|e: ParseIntError| e.to_string())?;
    if n < 0 {
        Err(format!("{} is negative", n))
    } else {
        Ok(n as u32)
    }
}

fn main() {
    let inputs = ["42", "-5", "abc", "100"];
    for s in &inputs {
        match parse_positive(s) {
            Ok(n)  => println!("{} -> Ok({})", s, n),
            Err(e) => println!("{} -> Err({})", s, e),
        }
    }

    // map, and_then, unwrap_or_else
    let doubled: Result = parse_positive("21").map(|n| n * 2);
    println!("{:?}", doubled); // Ok(42)

    // Convert Result to Option
    let opt: Option = parse_positive("5").ok();
    let res: Result = opt.ok_or("was None");
    println!("{:?} {:?}", opt, res);
}

🎯 Practice

  1. Write fn safe_sqrt(x: f64) -> Option<f64> - None if x < 0
  2. Write fn parse_range(s: &str) -> Result<(i32,i32), String> parsing "10..20" format
  3. Chain: parse a string to f64, take sqrt (Option), convert to Result, and_then check it is > 1

🎉 Key Takeaways