# Rust Enums --- ## 1. Basics ### what? Enums (enumerations) represent a type that can be one of several variants - Each variant can carry different data letting you capture different states in a single type - Provide type safety through pattern matching (handling all possible variants) key concept: enums provides a way to express `sum types` (multiple possible values, each possibly with data) --- ### Syntax ```rust enum Message { Quit, // Unit variant -- zero sized type Move { x: i32, y: i32 }, // Struct variant -- equivalent to a struct Write(String), // Tuple variant -- wrapped around a string ChangeColor(i32, i32, i32), // Tuple variant with multiple values -- tuple with 3 integers } ``` --- ## 2. Enum Variants --- ### Types of Variants 1. **Unit Variants** stateless - hold no data beyond their identity small memory footprint ```rust enum Direction { North, South, East, West, } ``` --- ```rust enum ConnectionStatus { Connected, Disconnected, } ``` --- 2. **Tuple Variants** ```rust enum Temperature { Celsius(f64), Fahrenheit(f64), Kelvin(f64), } // in this case, each variant is carrying a single f64 value ``` --- 3. **Struct Variants** ```rust enum Shape { Circle { radius: f64, center: (f64, f64) }, Rectangle { width: f64, height: f64 }, } ``` --- ## 3. Working with Enums --- ### Pattern Matching ```rust fn process_message(msg: Message) { match msg { Message::Quit => println!("Quitting"), Message::Move { x, y } => println!("Moving to ({}, {})", x, y), Message::Write(text) => println!("Text message: {}", text), Message::ChangeColor(r, g, b) => println!("Color changed to RGB({},{},{})", r, g, b), } } ``` --- ### if let well, just want to handle one pattern ```rust let some_value = Some(3); if let Some(value) = some_value { println!("Found value: {}", value); } ``` --- ## 4. Advanced Enum Features --- ### Generic Enums ```rust enum Result<T, E> { Ok(T), Err(E), } enum Option<T> { Some(T), None, } ``` - handle `value-or-none` or `success-or-error` without null or exceptions. --- ### Methods on Enums ```rust enum Status { Active(String), // holding a string value Inactive, // no value } impl Status { // is status active? fn is_active(&self) -> bool { matches!(self, Status::Active(_)) } // return name if active, none if inactive fn get_name(&self) -> Option<&String> { match self { Status::Active(name) => Some(name), Status::Inactive => None, } } } ``` --- ## 5. Best Practices --- ### The Option Enum Option<T> ```rust fn divide(numerator: f64, denominator: f64) -> Option<f64> { if denominator == 0.0 { None } else { Some(numerator / denominator) } } ``` avoiding null reference errors and force the caller to handle the possible absence of a value --- ### The Result Enum - core error handling pattern ```rust fn parse_port(s: &str) -> Result<u16, std::num::ParseIntError> { s.parse() } ``` - caller must handle either `Ok(u16)` or `Err(ParseIntError)` - can also use `?` to propagate errors neatly --- ### Combining Different Enum Patterns ```rust enum NetworkEvent { Connection { id: u32, status: Status, }, Message { content: String, priority: Option<u8>, }, Error(NetworkError), } enum NetworkError { Timeout(u32), InvalidProtocol(String), ConnectionLost, } ``` --- ## 6. Memory and performance considerations --- ### Null Pointer Optimization - example, certain enums like `Option<&T>`, optimized to the size of a single pointer - a plus? you don't pay extra emory overhead for using `Option` over raw pointers ```rust // Takes same space as *const T // in that it occupies the same space as a single pointer let x: Option<&T> = None; ``` --- ### Memory Layout ```rust enum Packet { Small(u32), Large(String), } // Size of Packet = size of largest variant + discriminant ``` --- ## 7. enum implementation samples ### State Machine Implementation ```rust enum State { Start, Processing { progress: f32 }, Done(String), Error(String), } impl State { fn next(self, input: &str) -> Self { match self { State::Start => State::Processing { progress: 0.0 }, State::Processing { progress } if progress >= 1.0 => { State::Done(input.to_string()) } State::Processing { progress } => { State::Processing { progress: progress + 0.1 } } State::Done(_) | State::Error(_) => self, } } } fn main() { let mut current_state = State::Start; println!("Initial: {:?}", current_state); for _ in 0..12 { current_state = current_state.next("Finished!"); println!("Current: {:?}", current_state); } } ``` --- ### Command Pattern ```rust enum Command { Save { filename: String }, Load { filename: String }, Undo, Redo, Copy { text: String }, Paste { position: usize }, } impl Command { fn execute(&self) -> Result<(), String> { match self { Command::Save { filename } => { println!("Saving to {}", filename); Ok(()) } Command::Load { filename } => { println!("Loading from {}", filename); Ok(()) } Command::Undo => { println!("Undoing last action"); Ok(()) } Command::Redo => { println!("Redoing last undone action"); Ok(()) } Command::Copy { text } => { println!("Copying text: {}", text); Ok(()) } Command::Paste { position } => { println!("Pasting at position {}", position); Ok(()) } } } } fn main() { let commands = vec![ Command::Save { filename: String::from("file1.txt") }, Command::Undo, Command::Copy { text: String::from("some text") }, Command::Paste { position: 42 }, Command::Redo, Command::Load { filename: String::from("file2.txt") }, ]; for cmd in commands { if let Err(e) = cmd.execute() { eprintln!("Command failed: {}", e); } } } ``` --- ## 8. Common Pitfalls and solutions maybe... --- ### Avoiding Pattern Match Exhaustion ```rust // Bad: Missing variants fn process_status(status: Status) { match status { Status::Active(_) => println!("Active"), // Missing Inactive case - won't compile } } // Good: Using wildcard pattern fn process_status(status: Status) { match status { Status::Active(name) => println!("Active: {}", name), _ => println!("Inactive"), } } ``` --- ### Dealing with Large Enum Variants If one variant is significantly larger than others, consider `boxing` that data to keep the enum size smaller. leading to more efficient memory usage when you store many enum instances. - btw, see boxing as like putting your stuff in a storage unit instead of keeping it all at home ```rust // Better memory efficiency for large data enum Message { Text(Box<String>), // Heap-allocated Binary(Box<Vec<u8>>), } ``` - Option<fn()> (function pointers) - Non-null raw pointers (NonNull<T>) --- github - nyakiomaina X - @nyakiomaina11 ig - @nyakio.codes
{"title":"Rust Enums","description":"Enums (enumerations) in Rust are a way to define a type that can be one of several variants. Unlike enums in many other languages, Rust enums are algebraic data types (ADTs) that can hold data within each variant.","contributors":"[{\"id\":\"9f60239b-5506-463b-b120-fdffe3093df3\",\"add\":10043,\"del\":2266}]"}
    147 views