Lesson 20 of 50 · rust
Panic and Unwinding
Duration: 12 minutes
Lesson 20 of 50 · rust
Duration: 12 minutes
panic! macro and strategies for handling panics.\n\n## The Panic Macro\n\nrust\nfn main() {\n panic!(\"Something went very wrong!\");\n // This line never executes\n}\n\n\nOutput:\n\nthread 'main' panicked at 'Something went very wrong!', src/main.rs:2:5\n\n\n## Panic From Unwrapping\n\nCommon ways panics occur:\n\nrust\nfn main() {\n let x: Option<i32> = None;\n let val = x.unwrap(); // panics: called `Option::unwrap()` on a `None` value\n \n let v = vec![1, 2, 3];\n let item = v[10]; // panics: index out of bounds\n \n let nums = \"abc\";\n let result: i32 = nums.parse().unwrap(); // panics: invalid digit\n}\n\n\n## When to Panic\n\n### Good Uses\n- Unrecoverable errors: No sensible recovery available\n- Development: Assert assumptions in tests\n- Examples/Prototypes: Simplify code for clarity\n\n### Bad Uses\n- Expected failures: Should use Result instead\n- File not found: Use Result and handle gracefully\n- Invalid input: Validate and return error\n\n## Assertions\n\nAssert conditions hold, panic if they don't:\n\nrust\nfn divide(a: i32, b: i32) -> i32 {\n assert!(b != 0, \"Division by zero!\");\n a / b\n}\n\nfn main() {\n // In debug builds, checks are run\n // In release builds, checks are removed (unsafe!)\n let result = divide(10, 2);\n println!(\"Result: {}\", result);\n}\n\n\n## Debug vs Release Panics\n\n### Debug Mode\nrust\nlet v = vec![1, 2, 3];\nlet item = v[99]; // panics\n\n\n### Release Mode\nOut-of-bounds access is undefined behavior (Rust still checks in safe code though).\n\n## Catching Panics\n\nYou can catch panics using catch_unwind:\n\nrust\nuse std::panic;\n\nfn main() {\n let result = panic::catch_unwind(|| {\n panic!(\"Oops!\")\n });\n \n match result {\n Ok(_) => println!(\"No panic\"),\n Err(_) => println!(\"Caught a panic!\"),\n }\n}\n\n\n## Panic Hooks\n\nCustomize panics with hooks:\n\nrust\nuse std::panic;\n\nfn main() {\n panic::set_hook(Box::new(|info| {\n if let Some(s) = info.payload().downcast_ref::<&str>() {\n eprintln!(\"Custom panic handler: {}\", s);\n }\n }));\n \n panic!(\"Something failed\");\n}\n\n\n## Unwinding vs Aborting\n\nTwo strategies for handling panics:\n\ntoml\n# In Cargo.toml\n[profile.dev]\npanic = \"unwind\" # Default: unwind stack\n\n[profile.release]\npanic = \"abort\" # Faster: immediately exit\n\n\nUnwind** (default):\n- Cleans up resources via drop\n- Slower compilation\n- Allows catching panics\n\nAbort:\n- Immediately terminates\n- Faster code\n- No cleanup\n\n## Best Practices\n\n1. Use Result for possible failures: Not panics\n2. Assert preconditions: In development\n3. Document panic conditions: In documentation\n4. Test panic scenarios: Ensure behavior is expected\n5. Avoid unwrap in libraries: Use Result propagation\n\n## Practical Example\n\nrust\nfn process_number(s: &str) -> Result<i32, String> {\n // Use Result for recoverable errors\n s.parse::<i32>()\n .map_err(|_| String::from(\"Not a valid number\"))\n}\n\nfn main() {\n // Handle Result properly\n match process_number(\"42\") {\n Ok(n) => println!(\"Got: {}\", n),\n Err(e) => println!(\"Error: {}\", e),\n }\n \n // Only use unwrap in examples\n let num = process_number(\"99\").unwrap(); // OK for examples\n}\n