Enumeraciones en Rust
⏱ Dedicación recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.
En Rust, las enumeraciones (enums) también se utilizan para representar tipos suma, permitiendo definir múltiples variantes que representan diferentes estados o condiciones. Aunque tanto Kotlin como Rust ofrecen esta funcionalidad, hay diferencias notables en cómo se implementan y se usan las enumeraciones en cada lenguaje.
Definición y Uso Básico
En Rust, las enumeraciones se definen de manera similar a otros lenguajes, pero con algunas diferencias clave:
enum DeliveryState {
Pending,
Paid,
Shipped,
Delivered,
Cancelled,
}
Aquí, DeliveryState
es una enumeración que contiene las mismas variantes que en Kotlin, pero Rust permite hacer más cosas con estas variantes:
- Datos Asociados: Rust permite que cada variante de una enumeración contenga datos asociados, algo similar a lo que Kotlin hace con los constructores en enumeraciones, pero en Rust, esto es más flexible.
- Patrones de coincidencia exhaustivos: Al igual que en Kotlin, Rust también garantiza que las coincidencias sean exhaustivas. Si no se cubren todas las variantes de una enumeración, el compilador mostrará un error.
Patrones de Coincidencia
En Rust, se utiliza la expresión match
para manejar enumeraciones de manera exhaustiva, similar a when
en Kotlin. Sin embargo, a diferencia de Kotlin, Rust obliga a que todos los casos sean manejados, incluso si se usa un valor por defecto:
fn handle_order_state(state: DeliveryState) {
match state {
DeliveryState::Pending => println!("Order is pending"),
DeliveryState::Paid => println!("Order is paid"),
DeliveryState::Shipped => println!("Order is shipped"),
DeliveryState::Delivered => println!("Order is delivered"),
DeliveryState::Cancelled => println!("Order is cancelled"),
}
}
Métodos en Enumeraciones
En Rust, es posible implementar métodos en las enumeraciones, similar a Kotlin. Sin embargo, a diferencia de Kotlin, Rust no permite agregar métodos abstractos directamente en las variantes. En lugar de eso, los métodos se definen en una impl block para la enumeración completa:
impl DeliveryState {
fn signal(&self) -> &str {
match self {
DeliveryState::Pending => "Order is pending",
DeliveryState::Paid => "Order is paid",
DeliveryState::Shipped => "Order is shipped",
DeliveryState::Delivered => "Order is delivered",
DeliveryState::Cancelled => "Order is cancelled",
}
}
fn is_final_state(&self) -> bool {
matches!(self, DeliveryState::Delivered | DeliveryState::Cancelled)
}
}
Definición de Enumeraciones con Datos Asociados en Rust
En Rust, cada variante de una enumeración puede tener tipos de datos asociados, lo que permite almacenar información adicional para cada valor de la enumeración. A diferencia de Kotlin, donde los parámetros se definen en el constructor, en Rust los datos asociados se especifican directamente en cada variante.
enum DeliveryState {
Pending(String, u32),
Cancelled(String, u32),
}
- [2]
Pending(String, u32)
: La variantePending
lleva unString
que podría describir el estado y unu32
como un código. - [3]
Cancelled(String, u32)
: Similarmente, la varianteCancelled
tiene unString
y unu32
.
Uso de las Variantes con Datos Asociados
Para acceder y utilizar los datos de cada variante en Rust, se utiliza pattern matching (match
). Esto permite extraer y trabajar con los datos asociados a cada variante de manera explícita.
Ejemplo de Uso:
fn print_order_details(state: DeliveryState) {
match state {
DeliveryState::Pending(description, code) => {
println!("State: {}, Code: {}", description, code);
},
DeliveryState::Cancelled(description, code) => {
println!("State: {}, Code: {}", description, code);
},
}
}
- [2-9]: Se utiliza
match
para verificar y extraer los valores de cada variante. - [3-5, 6-8]: Se desestructuran los datos asociados (
description
ycode
) de las variantesPending
yCancelled
, respectivamente.
Definición de Traits y su Implementación en Enumeraciones
En Rust, se definen traits para establecer comportamientos comunes. Luego, se implementan para cada variante o para la enumeración en general.
trait Notifier {
fn notify(&self, message: &str);
}
trait Storable {
fn store(&self);
}
enum DeliveryState {
Pending,
Paid,
Shipped,
Delivered,
Cancelled,
}
impl Notifier for DeliveryState {
fn notify(&self, message: &str) {
println!("Notifying {:?}: {}", self, message);
}
}
impl Storable for DeliveryState {
fn store(&self) {
println!("Storing state: {:?}", self);
}
}
impl DeliveryState {
fn signal(&self) -> &str {
match self {
DeliveryState::Pending => "Order is pending",
DeliveryState::Paid => "Order is paid",
DeliveryState::Shipped => "Order is shipped",
DeliveryState::Delivered => "Order is delivered",
DeliveryState::Cancelled => "Order is cancelled",
}
}
fn is_final_state(&self) -> bool {
matches!(self, DeliveryState::Delivered | DeliveryState::Cancelled)
}
}
fn main() {
let state = DeliveryState::Paid;
println!("{}", state.signal());
state.notify("Order is paid");
state.store();
println!("{}", state.is_final_state());
/*
Output:
Order is paid
Notifying Paid: Order is paid
Storing state: Paid
false
*/
}
Comparación Final
Característica | Rust | Kotlin |
---|---|---|
Definición de variantes | Las variantes se definen en el enum , y cada una puede tener datos asociados específicos. | Las variantes se definen con un constructor compartido en el enum , pero no permiten diferentes tipos de datos por variante. |
Datos asociados | Permite asociar datos distintos a cada variante, lo que proporciona flexibilidad en el diseño. | Permite un solo conjunto de parámetros comunes para todas las variantes en el constructor. |
Métodos en enumeraciones | Los métodos se implementan mediante bloques impl y se aplican a la enumeración completa. | Los métodos pueden definirse directamente en la enumeración y cada variante puede tener sus propios métodos. |
Implementación de interfaces | Usa traits para definir comportamientos que se implementan en las variantes o en el enum en general. | Las enumeraciones pueden implementar interfaces directamente, heredando métodos predeterminados. |
Patrones de coincidencia | Usa match para manejar variantes de manera exhaustiva, asegurando que todas se manejen. | Utiliza when para verificar las variantes, con verificación exhaustiva similar a Rust. |
Flexibilidad en el uso de variantes | Cada variante puede llevar distintos tipos de datos y se accede a ellos usando match para desestructurar. | Se accede a los valores del constructor de forma similar a las propiedades de una clase. |
Beneficios
- Flexibilidad con datos asociados: Rust permite que cada variante de una enumeración tenga diferentes tipos y cantidades de datos asociados, lo que proporciona una gran flexibilidad en el diseño y uso de las variantes.
- Patrones de coincidencia exhaustivos: El uso del patrón
match
garantiza que todas las variantes sean manejadas, reduciendo el riesgo de errores en tiempo de ejecución y haciendo que el código sea más seguro y predecible. - Integración con traits: Rust permite que las enumeraciones implementen traits, lo que posibilita extender el comportamiento y compartir funcionalidades entre las variantes de manera estructurada y reutilizable.
- Eficiencia y control: Al permitir un manejo explícito de los datos y comportamientos en las variantes, Rust proporciona un control granular sobre cómo se manejan y acceden las propiedades de cada variante.
Limitaciones
- Complejidad en el manejo de datos asociados: Aunque es flexible, el uso de datos asociados en cada variante puede añadir complejidad, especialmente cuando se deben manejar muchas variantes diferentes con distintos tipos de datos.
- Verbosidad en la implementación: La implementación de métodos en un bloque
impl
para enumeraciones puede ser más verbosa que en otros lenguajes, ya que requiere un manejo explícito de las variantes a través dematch
. - Curva de aprendizaje: La combinación de traits, bloques
impl
, y patrones de coincidencia exhaustivos puede resultar compleja para quienes no están familiarizados con el enfoque de Rust, lo que puede aumentar la curva de aprendizaje inicial. - Limitaciones en métodos específicos por variante: A diferencia de Kotlin, donde cada variante puede tener sus propios métodos específicos, en Rust los métodos se aplican a la enumeración completa, limitando las capacidades para métodos específicos en variantes individuales.
¿Qué Aprendimos?
En esta lección, hemos comparado las enumeraciones en Rust y Kotlin, destacando las similitudes y diferencias clave entre ambos lenguajes:
- Flexibilidad y Datos Asociados: Mientras que Kotlin permite constructores en las enumeraciones para asociar información a las variantes, Rust proporciona una mayor flexibilidad al permitir tipos de datos asociados distintos en cada variante. Esto hace que las enumeraciones en Rust sean más versátiles para casos complejos.
- Patrones de Coincidencia Exhaustivos: Tanto Rust como Kotlin garantizan que las coincidencias en las enumeraciones sean exhaustivas, pero Rust se asegura de que todos los casos sean manejados estrictamente, evitando errores en tiempo de ejecución.
- Implementación de Métodos y Traits: Las enumeraciones en Rust pueden implementar métodos a través de bloques
impl
y se pueden extender con traits, lo que permite un comportamiento compartido entre variantes. En Kotlin, las enumeraciones pueden implementar interfaces directamente, heredando comportamientos predeterminados.
La elección entre usar Rust o Kotlin para trabajar con enumeraciones dependerá del contexto y de las necesidades del proyecto. Rust ofrece más flexibilidad y un control detallado, pero con una curva de aprendizaje más pronunciada y una mayor complejidad en el manejo de datos asociados y patrones. Kotlin, por otro lado, ofrece una sintaxis más directa y familiar para quienes están acostumbrados a trabajar con lenguajes orientados a objetos.
Esta comparación nos ayuda a entender cómo las decisiones de diseño en los lenguajes afectan la manera en que manejamos y utilizamos las enumeraciones en el desarrollo de software.
Bibliografías Recomendadas
- 📚 "6. Enums and Pattern Matching". (2023). S. Klabnik & C. Nichols, en The Rust Programming Language, (pp. 103–118.) No Starch Press.