In Rust, enums can hold not only values (variants) but also associated data (payload). This makes them particularly convenient for creating algebraic data types, for example, to represent state or messages with parameters. For example:
enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), }
To process such enums, the match operator is usually applied, allowing unpacking of the nested values of each variant:
let msg = Message::Move { x: 10, y: 20 }; match msg { Message::Quit => println!("Quit message"), Message::Move { x, y } => println!("Move to ({}, {})", x, y), Message::Write(text) => println!("Text: {}", text), Message::ChangeColor(r, g, b) => println!("Change color to ({}, {}, {})", r, g, b), }
This approach allows for safe and explicit handling of all possible cases, minimizing errors in program logic.
Why is there no null for enums in Rust, and what will happen if you try to match against a non-existent variant?
A common incorrect answer: "The compiler will let the error pass if not all enum variants are handled in match."
In fact, the compiler will issue an error if not all possible variants are taken into account, or it will require you to add a catch-all (_). For example:
enum Status { Ok, Err(String) } let st = Status::Ok; match st { Status::Ok => println!("Ok"), // If you forget Status::Err, the compiler will complain }
Story In one project, when expanding the enum, the new variant wasn't added to the match handling in all the places it was used. This led to a panic! in release, as the
matchwas not exhaustive, while the tests did not cover all cases.
Story In another case, there was an attempt to compare enums using a regular
==without implementing PartialEq for complex variants with associated data, which caused an unexpected compilation error.
Story The team used a catch-all (
_) for handling variants, and later, when new variants were added, it resulted in the logic silently ignoring new cases, leading to hard-to-find defects.