In this lesson, you will learn how generic enums allow Option and Result to represent any data type while maintaining strict type safety.
The Power of Generic Enums
Up until now, you have seen how generics allow structs to hold different types of data. Enums take this a step further. Instead of just holding a fixed type, a generic enum can hold a "placeholder" type, which is defined only when you actually use the enum.
The two most important tools in your Rust toolkit—Option<T> and Result<T, E>—are implemented exactly this way.
Option: Representing Presence
The Option<T> enum is generic over T. It represents the possibility of a value existing (Some(T)) or being absent (None). Because it is generic, the same definition works for an integer, a string, or a complex struct:
let name: Option<String> = Some(String::from("Rust"));
let age: Option<i32> = None;
When you wrap a value in Option<T>, you are essentially saying: "This variable might hold a value of type T, and I promise to handle the case where it doesn't."
Result: Representing Outcomes
The Result<T, E> enum is generic over two types: T (the success type) and E (the error type). This is the foundation of Rust's error handling. It allows a function to return "either a valid piece of data or an error," without needing to guess what those might be.
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> {
if denominator == 0.0 {
Err(String::from("Cannot divide by zero"))
} else {
Ok(numerator / denominator)
}
}
Because Result is generic, you can define specific error enums for your system or simply use String for quick debugging, all while the compiler enforces that you check the error before accessing the inner value. By using these generic enums, you eliminate the "null pointer" class of bugs common in other languages, because the compiler forces you to handle the None or Err case before you can reach the data.
Key Takeaway: Generic enums like Option and Result decouple the logic of "success/failure" or "presence/absence" from the specific data types being handled, ensuring type safety regardless of the information being stored.