Enum (enumeraciones) en Rust son radicalmente diferentes de enum en C/C++: pueden almacenar datos asociados y son ideales para modelar estados y errores. Con ellos se construyen máquinas de estado finito tipo-seguras, diferentes tipos de Option/Result, se implementa el patrón "tipos suma". Históricamente, construcciones similares se han utilizado en lenguajes funcionales para describir variantes de una entidad con variantes estrictamente separadas.
Problema: lograr expresividad (expresar todas las variantes de estado), donde cada caso de tratamiento es obligatorio, y no es posible omitir accidentalmente una rama. Es difícil tipificar errores en toda la aplicación sin tal estructura expresiva.
Solución: enums con datos asociados y pattern matching dan control: cada rama es verificada por el compilador, se asegura la exhaustividad. Además, se han implementado una gran cantidad de métodos auxiliares para Result y Option.
Ejemplo de código:
enum NetworkState { Disconnected, Connecting(u32), // intento número Connected(String), Error(String), } fn print_state(state: NetworkState) { match state { NetworkState::Disconnected => println!("Red: desconectada"), NetworkState::Connecting(count) => println!("Red: conectando (intento {})", count), NetworkState::Connected(addr) => println!("Red: conectada a {}", addr), NetworkState::Error(msg) => println!("Error de red: {}", msg), } }
Características clave:
¿Se pueden manejar parcialmente ramas de enum sin _?
El compilador prohíbe los casos no cerrados para enums no exhaustivos, pero si se usa _, las ramas no tratadas serán "absorbidas". Se debe evitar _ en ramas clínicamente relevantes para que cambios futuros no queden sin ser notados.
¿En qué casos los valores asociados se refieren, y en cuáles se copian durante el pattern matching?
Durante el pattern matching, los datos asociados se mueven (move) por defecto. Si solo se necesita ver, utiliza referencias:
match &state { NetworkState::Connected(addr) => println!("por referencia: {}", addr), _ => {} }
¿Se pueden usar dos enums con variantes superpuestas por nombre en una sola estructura?
Sí, pero los nombres de las variantes se utilizan con el prefijo del enum. Esto evita colisiones y hace que el código sea auto-documentado (por ejemplo, Status::Ok vs NetworkState::Ok).
_ para ocultar cada vez más variantes al ampliar el enum_ =>) en controladores de errores críticosEn el código, el manejo de Result<T, E> siempre tiene catch-all a través de _ =>, y nuevos errores (al ampliar el enum) pasan desapercibidos: ocurren pérdidas silenciosas de errores.
Ventajas:
Desventajas:
Se utilizan exhaustiveness matching, cada variante del Enum se trata explícitamente, o panic en la rama para la que no hay un comportamiento definido.
Ventajas:
Desventajas: