Skip to main content
Brave Programmer Logo

BraveProgrammer

BraveProgrammer

HomeProjectsBlogsCoursesLessonsAbout

Site footer

BraveProgrammer

Free coding courses, practical tutorials, and real projects from BraveProgrammer. Learn web development with React, Next.js, and TypeScript.

Navigation

  • Home
  • Projects
  • Blogs
  • Courses

Resources

  • About
  • Lessons

© 2026 BraveProgrammer. All rights reserved.

  1. Courses
  2. /
  3. Rust Fundamentals

Lesson 18 of 50 · rust

Error Handling with Option

Duration: 15 minutes

Error Handling with Option\n\nOption<T> represents a value that may or may not exist. It replaces null pointers from other languages, making the absence of a value explicit in the type system.\n\n## The Option Type\n\nrust\nenum Option<T> {\n Some(T),\n None,\n}\n\n\nIt's so common you don't need to prefix it with std::option::\n\n## Creating Options\n\nrust\nfn main() {\n let some_number = Some(5);\n let none_number: Option<i32> = None;\n \n let x: Option<String> = Some(String::from(\"hello\"));\n let y: Option<String> = None;\n}\n\n\n## Matching Options\n\nThe most explicit way to handle Option values:\n\nrust\nfn find_even(nums: &[i32]) -> Option<i32> {\n for &n in nums {\n if n % 2 == 0 {\n return Some(n);\n }\n }\n None\n}\n\nfn main() {\n let numbers = [1, 3, 5, 8, 9];\n match find_even(&numbers) {\n Some(e) => println!(\"First even: {}\", e),\n None => println!(\"No even numbers\"),\n }\n}\n\n\n## If Let Shorthand\n\nFor cases where you only care about one variant:\n\nrust\nfn main() {\n let x: Option<i32> = Some(5);\n \n // Verbose match\n match x {\n Some(n) => println!(\"Value: {}\", n),\n None => (),\n }\n \n // Concise if let\n if let Some(n) = x {\n println!(\"Value: {}\", n);\n }\n}\n\n\n## Option Methods\n\nrust\nfn main() {\n let x: Option<i32> = Some(5);\n let y: Option<i32> = None;\n \n // Check existence\n println!(\"x.is_some(): {}\", x.is_some()); // true\n println!(\"y.is_none(): {}\", y.is_none()); // true\n \n // Extract with unwrap (panics if None)\n let val = x.unwrap(); // 5\n // let bad = y.unwrap(); // panics!\n \n // Safe extraction with default\n let val2 = y.unwrap_or(0); // 0\n let val3 = y.unwrap_or_else(|| 42); // 42\n \n // Transform values\n let doubled = x.map(|n| n * 2); // Some(10)\n let none_doubled = y.map(|n| n * 2); // None\n \n // Chain operations\n x.map(|n| n * 2)\n .map(|n| n + 1)\n .map(|n| println!(\"Result: {}\", n)); // None\n}\n\n\n## Practical Example: Parsing Configuration\n\nrust\nfn get_config_value(config: &std::collections::HashMap<String, String>, key: &str) -> Option<i32> {\n config.get(key)\n .and_then(|val| val.parse::<i32>().ok())\n}\n\nfn main() {\n let mut config = std::collections::HashMap::new();\n config.insert(String::from(\"timeout\"), String::from(\"30\"));\n \n match get_config_value(&config, \"timeout\") {\n Some(timeout) => println!(\"Timeout: {} seconds\", timeout),\n None => println!(\"Invalid or missing timeout\"),\n }\n}\n\n\n## The Question Mark Operator\n\nFor propagating None values early:\n\nrust\nfn main() {\n let x: Option<i32> = Some(5);\n \n // Traditional approach\n let y = match x {\n Some(val) => Some(val * 2),\n None => return None,\n };\n \n // But Option<T> expects the function to also return Option\n}\n\nfn double_optional(x: Option<i32>) -> Option<i32> {\n Some(x? * 2) // Returns None if x is None\n}\n\n\n## Why Option Over Null?\n\n1. Explicit: Absence is part of the type\n2. Safe: Can't access None as a value\n3. Composable: Chain operations with map, and_then\n4. Forces handling: Compiler requires you to handle None case

Previous: Collections: HashMapNext: Error Handling with Result