Rust
Tutorial Rust: Enumeraciones enums
Descubre cómo usar enumeraciones enums en Rust para modelar datos y comportamientos complejos con ejemplos prácticos y métodos.
Aprende Rust y certifícateVariantes simples
Las enumeraciones (o enums) son un tipo de dato en Rust que permite definir un tipo que puede ser uno de varios valores posibles, llamados variantes. Imagina un tipo que representa los días de la semana o los estados de un proceso - estos son casos perfectos para usar enums.
En su forma más básica, los enums en Rust son similares a los enums que encontrarías en lenguajes como C o Java. Veamos cómo se definen:
enum DiaSemana {
Lunes,
Martes,
Miercoles,
Jueves,
Viernes,
Sabado,
Domingo,
}
En este ejemplo, hemos creado un tipo DiaSemana
que puede tener exactamente uno de los siete valores definidos. Cada variante (Lunes, Martes, etc.) se convierte en un posible valor para este tipo.
Para crear un valor de este enum, usamos la sintaxis con el operador de ámbito ::
:
fn main() {
let dia = DiaSemana::Miercoles;
// Podemos usar el enum en una expresión if con comparaciones
if dia == DiaSemana::Viernes {
println!("¡Por fin es viernes!");
} else {
println!("Todavía no es viernes...");
}
}
Los enums son especialmente útiles cuando queremos representar un conjunto fijo de posibilidades. Por ejemplo, podríamos representar los estados de una tarea:
enum EstadoTarea {
NuevaCreada,
EnProgreso,
EnRevision,
Completada,
Cancelada,
}
fn main() {
let mi_tarea = EstadoTarea::EnProgreso;
// Podemos usar un enum en una estructura de control básica
match mi_tarea {
EstadoTarea::Completada => println!("¡La tarea está terminada!"),
EstadoTarea::Cancelada => println!("La tarea fue cancelada"),
_ => println!("La tarea sigue en proceso"),
}
}
En este ejemplo, usamos la expresión match
para realizar diferentes acciones según el valor del enum. El patrón _
actúa como un comodín que captura cualquier valor no especificado anteriormente.
Enums con valores explícitos
En algunos casos, podemos querer asignar valores específicos a las variantes de un enum, similar a cómo funcionan en C:
enum Codigo {
Ok = 200,
NotFound = 404,
ServerError = 500,
}
fn main() {
// Podemos obtener el valor numérico subyacente
let codigo_respuesta = Codigo::NotFound as i32;
println!("Código de estado: {}", codigo_respuesta); // Imprime: Código de estado: 404
}
Esto es útil cuando necesitamos interoperar con sistemas que esperan valores numéricos específicos, como códigos de estado HTTP o códigos de error.
Enums para modelar dominios
Los enums simples son excelentes para modelar dominios donde tenemos un conjunto fijo de posibilidades sin datos adicionales. Por ejemplo, podríamos representar los tipos de usuario en un sistema:
enum TipoUsuario {
Administrador,
Editor,
Autor,
Suscriptor,
Invitado,
}
fn verificar_permisos(usuario: TipoUsuario, accion: &str) -> bool {
match (usuario, accion) {
(TipoUsuario::Administrador, _) => true, // Los administradores pueden hacer cualquier cosa
(TipoUsuario::Editor, "editar") => true,
(TipoUsuario::Editor, "publicar") => true,
(TipoUsuario::Autor, "editar") => true,
_ => false, // Cualquier otra combinación no está permitida
}
}
fn main() {
let usuario = TipoUsuario::Autor;
if verificar_permisos(usuario, "editar") {
println!("Permiso concedido para editar");
} else {
println!("Permiso denegado");
}
if verificar_permisos(usuario, "publicar") {
println!("Permiso concedido para publicar");
} else {
println!("Permiso denegado");
}
}
En este ejemplo, usamos un enum para representar los diferentes tipos de usuarios y luego implementamos una función que verifica los permisos basándose en el tipo de usuario y la acción solicitada.
Iterando sobre enums
A diferencia de algunos otros lenguajes, Rust no proporciona una forma automática de iterar sobre todas las variantes de un enum. Sin embargo, podemos implementar esta funcionalidad nosotros mismos si es necesario:
enum Direccion {
Norte,
Sur,
Este,
Oeste,
}
fn main() {
// Creamos un array con todas las variantes
let direcciones = [
Direccion::Norte,
Direccion::Sur,
Direccion::Este,
Direccion::Oeste,
];
// Ahora podemos iterar sobre el array
for dir in &direcciones {
match dir {
Direccion::Norte => println!("Viajando hacia el norte"),
Direccion::Sur => println!("Viajando hacia el sur"),
Direccion::Este => println!("Viajando hacia el este"),
Direccion::Oeste => println!("Viajando hacia el oeste"),
}
}
}
Los enums simples son una herramienta fundamental en Rust para modelar dominios con un conjunto fijo de posibilidades. En la siguiente sección, veremos cómo Rust lleva los enums al siguiente nivel permitiendo asociar datos a cada variante, lo que los hace mucho más poderosos que los enums tradicionales de otros lenguajes.
Variantes con datos
Mientras que los enums simples son útiles para representar un conjunto fijo de valores, Rust lleva este concepto mucho más allá permitiendo que cada variante del enum pueda contener datos asociados. Esta característica distintiva de Rust hace que los enums sean extremadamente versátiles y expresivos.
En lugar de limitarse a ser simples etiquetas, las variantes de un enum pueden contener cualquier tipo de datos, incluyendo tipos primitivos, estructuras, tuplas e incluso otros enums. Cada variante puede tener su propia estructura de datos independiente.
Enums con datos homogéneos
Comencemos con un ejemplo sencillo donde todas las variantes contienen el mismo tipo de datos:
enum Calificacion {
Sobresaliente(f32),
Notable(f32),
Aprobado(f32),
Suspenso(f32),
}
fn main() {
let nota_programacion = Calificacion::Notable(8.5);
// Podemos extraer el valor usando match
let mensaje = match nota_programacion {
Calificacion::Sobresaliente(valor) => format!("¡Excelente! Has sacado un {}", valor),
Calificacion::Notable(valor) => format!("Muy bien, has obtenido un {}", valor),
Calificacion::Aprobado(valor) => format!("Has aprobado con un {}", valor),
Calificacion::Suspenso(valor) => format!("Has suspendido con un {}", valor),
};
println!("{}", mensaje);
}
En este ejemplo, cada variante del enum Calificacion
contiene un valor de tipo f32
que representa la nota numérica. Cuando hacemos el match
, podemos extraer y utilizar ese valor.
Enums con datos heterogéneos
La verdadera potencia de los enums en Rust se manifiesta cuando cada variante puede contener diferentes tipos de datos:
enum Mensaje {
Texto(String),
CodigoDeError(i32),
Coordenadas(i32, i32),
Configuracion { id: i32, valor: bool },
}
fn main() {
let mensaje1 = Mensaje::Texto(String::from("Hola, ¿cómo estás?"));
let mensaje2 = Mensaje::CodigoDeError(404);
let mensaje3 = Mensaje::Coordenadas(10, 20);
let mensaje4 = Mensaje::Configuracion { id: 1, valor: true };
procesar_mensaje(mensaje1);
procesar_mensaje(mensaje2);
}
fn procesar_mensaje(msg: Mensaje) {
match msg {
Mensaje::Texto(contenido) => println!("Mensaje de texto: {}", contenido),
Mensaje::CodigoDeError(codigo) => println!("Error: {}", codigo),
Mensaje::Coordenadas(x, y) => println!("Posición: ({}, {})", x, y),
Mensaje::Configuracion { id, valor } => {
println!("Configuración {}: {}", id, if valor { "activada" } else { "desactivada" })
}
}
}
Observa cómo cada variante puede tener una estructura completamente diferente:
Texto
contiene unaString
CodigoDeError
contiene un enteroCoordenadas
contiene una tupla de dos enterosConfiguracion
contiene una estructura similar a un struct
Esta flexibilidad nos permite modelar conceptos complejos de manera elegante y segura.
Modelando estados con datos
Los enums con datos son perfectos para modelar estados que contienen información específica para cada estado:
enum EstadoJuego {
Inicio,
Jugando { puntuacion: i32, nivel: i32, vidas: i32 },
Pausa { tiempo_transcurrido: f32 },
GameOver { puntuacion_final: i32 },
}
fn main() {
let mut estado = EstadoJuego::Inicio;
// Simulamos cambios de estado
estado = EstadoJuego::Jugando { puntuacion: 0, nivel: 1, vidas: 3 };
// Después de jugar un rato
if let EstadoJuego::Jugando { puntuacion, nivel, vidas } = estado {
println!("Jugando nivel {} con {} puntos y {} vidas", nivel, puntuacion, vidas);
// Perdemos el juego
if vidas <= 0 {
estado = EstadoJuego::GameOver { puntuacion_final: puntuacion };
}
}
}
En este ejemplo, el estado Jugando
contiene toda la información relevante para ese estado específico, mientras que otros estados contienen información diferente o ninguna.
Enums recursivos
Una característica particularmente potente es que los enums pueden ser recursivos, es decir, pueden contener instancias de sí mismos. Esto es útil para representar estructuras de datos jerárquicas:
enum ArbolBinario {
Hoja(i32),
Nodo {
valor: i32,
izquierda: Box<ArbolBinario>,
derecha: Box<ArbolBinario>,
},
}
fn main() {
// Creamos un árbol simple:
// 5
// / \
// 3 7
let arbol = ArbolBinario::Nodo {
valor: 5,
izquierda: Box::new(ArbolBinario::Hoja(3)),
derecha: Box::new(ArbolBinario::Hoja(7)),
};
// Calculamos la suma de todos los valores
let suma = suma_arbol(&arbol);
println!("La suma de todos los valores es: {}", suma);
}
fn suma_arbol(arbol: &ArbolBinario) -> i32 {
match arbol {
ArbolBinario::Hoja(valor) => *valor,
ArbolBinario::Nodo { valor, izquierda, derecha } => {
*valor + suma_arbol(izquierda) + suma_arbol(derecha)
}
}
}
Aquí usamos Box<T>
para crear un tipo de puntero inteligente que nos permite tener estructuras recursivas. Sin entrar en detalles de cómo funciona la gestión de memoria, esto nos permite crear estructuras de datos complejas como árboles.
Enums para resultados de operaciones
Un patrón común en Rust es usar enums para representar el resultado de operaciones que pueden fallar:
enum ResultadoDivision {
Exito(f64),
ErrorDivisionPorCero,
}
fn dividir(a: f64, b: f64) -> ResultadoDivision {
if b == 0.0 {
ResultadoDivision::ErrorDivisionPorCero
} else {
ResultadoDivision::Exito(a / b)
}
}
fn main() {
let resultado1 = dividir(10.0, 2.0);
let resultado2 = dividir(5.0, 0.0);
match resultado1 {
ResultadoDivision::Exito(valor) => println!("El resultado es: {}", valor),
ResultadoDivision::ErrorDivisionPorCero => println!("¡Error! División por cero"),
}
match resultado2 {
ResultadoDivision::Exito(valor) => println!("El resultado es: {}", valor),
ResultadoDivision::ErrorDivisionPorCero => println!("¡Error! División por cero"),
}
}
Este patrón es tan útil que Rust tiene enums predefinidos extremadamente útiles como Option
y Result
que veremos en la próxima lección.
Usando if let para simplificar el manejo de enums
Cuando solo nos interesa una variante específica de un enum, podemos usar la sintaxis if let
para simplificar nuestro código:
enum Configuracion {
Simple,
Avanzada { max_conexiones: i32, timeout: i32 },
Personalizada(String),
}
fn main() {
let config = Configuracion::Avanzada { max_conexiones: 100, timeout: 30 };
// En lugar de un match completo:
/*
match config {
Configuracion::Avanzada { max_conexiones, timeout } => {
println!("Configuración avanzada: {} conexiones, {} segundos", max_conexiones, timeout);
},
_ => (), // No hacemos nada para otros casos
}
*/
// Podemos usar if let:
if let Configuracion::Avanzada { max_conexiones, timeout } = config {
println!("Configuración avanzada: {} conexiones, {} segundos", max_conexiones, timeout);
}
}
La sintaxis if let
es más concisa cuando solo nos interesa una variante específica y queremos ignorar las demás.
Los enums con datos son una de las características más distintivas y potentes de Rust, permitiéndonos modelar dominios complejos de manera segura y expresiva. En la siguiente sección, veremos cómo podemos añadir comportamiento a nuestros enums implementando métodos, igual que hacemos con los structs.
Métodos en enums
Al igual que con las estructuras (struct
), Rust nos permite implementar métodos para nuestros enums. Esta capacidad convierte a los enums en tipos de datos no solo con diferentes variantes, sino también con comportamiento asociado, lo que aumenta significativamente su utilidad y expresividad.
La sintaxis para implementar métodos en enums es idéntica a la que ya conocemos para structs, utilizando el bloque impl
:
enum Figura {
Circulo(f64),
Rectangulo(f64, f64),
Triangulo(f64, f64, f64),
}
impl Figura {
fn area(&self) -> f64 {
match self {
Figura::Circulo(radio) => std::f64::consts::PI * radio * radio,
Figura::Rectangulo(ancho, alto) => ancho * alto,
Figura::Triangulo(a, b, c) => {
// Fórmula de Herón para el área de un triángulo
let s = (a + b + c) / 2.0;
(s * (s - a) * (s - b) * (s - c)).sqrt()
}
}
}
fn describir(&self) -> String {
match self {
Figura::Circulo(radio) => format!("Círculo con radio {}", radio),
Figura::Rectangulo(ancho, alto) => format!("Rectángulo de {}x{}", ancho, alto),
Figura::Triangulo(a, b, c) => format!("Triángulo con lados {}, {} y {}", a, b, c),
}
}
}
fn main() {
let circulo = Figura::Circulo(5.0);
let rectangulo = Figura::Rectangulo(4.0, 6.0);
println!("{}: área = {:.2}", circulo.describir(), circulo.area());
println!("{}: área = {:.2}", rectangulo.describir(), rectangulo.area());
}
En este ejemplo, hemos definido dos métodos para nuestro enum Figura
:
area()
- calcula el área según el tipo de figuradescribir()
- genera una descripción textual de la figura
Métodos específicos para variantes
Aunque los métodos se implementan para todo el enum, podemos crear comportamientos específicos para cada variante utilizando patrones de coincidencia dentro de los métodos:
enum Mensaje {
Texto(String),
Audio { contenido: Vec<u8>, duracion_segundos: u32 },
Imagen { url: String, ancho: u32, alto: u32 },
}
impl Mensaje {
fn enviar(&self) -> bool {
println!("Enviando mensaje...");
// Comportamiento específico según la variante
match self {
Mensaje::Texto(contenido) => {
println!("Enviando texto: {}", contenido);
// Lógica para enviar texto
true
},
Mensaje::Audio { contenido, duracion_segundos } => {
println!("Enviando audio de {} segundos ({} bytes)",
duracion_segundos, contenido.len());
// Lógica para enviar audio
true
},
Mensaje::Imagen { url, ancho, alto } => {
println!("Enviando imagen {}x{} desde: {}", ancho, alto, url);
// Lógica para enviar imagen
if url.starts_with("http") {
true
} else {
println!("Error: URL inválida");
false
}
}
}
}
fn tamanio_bytes(&self) -> usize {
match self {
Mensaje::Texto(contenido) => contenido.len(),
Mensaje::Audio { contenido, .. } => contenido.len(),
Mensaje::Imagen { .. } => 0, // Asumimos que la imagen es un enlace externo
}
}
}
fn main() {
let mensaje1 = Mensaje::Texto(String::from("Hola, ¿cómo estás?"));
let mensaje2 = Mensaje::Imagen {
url: String::from("https://ejemplo.com/imagen.jpg"),
ancho: 800,
alto: 600
};
mensaje1.enviar();
println!("Tamaño del mensaje: {} bytes", mensaje1.tamanio_bytes());
mensaje2.enviar();
}
Métodos constructores
Una práctica común es implementar métodos constructores (funciones asociadas sin self
) que facilitan la creación de instancias específicas del enum:
enum Resultado<T> {
Ok(T),
Error(String),
}
impl<T> Resultado<T> {
// Método constructor para crear un resultado exitoso
fn ok(valor: T) -> Resultado<T> {
Resultado::Ok(valor)
}
// Método constructor para crear un resultado de error
fn error(mensaje: &str) -> Resultado<T> {
Resultado::Error(mensaje.to_string())
}
// Método para verificar si es un resultado exitoso
fn es_ok(&self) -> bool {
match self {
Resultado::Ok(_) => true,
Resultado::Error(_) => false,
}
}
// Método para obtener el valor o un valor por defecto
fn valor_o_defecto(&self, defecto: T) -> T
where
T: Clone,
{
match self {
Resultado::Ok(valor) => valor.clone(),
Resultado::Error(_) => defecto,
}
}
}
fn dividir(a: f64, b: f64) -> Resultado<f64> {
if b == 0.0 {
Resultado::error("División por cero")
} else {
Resultado::ok(a / b)
}
}
fn main() {
let resultado1 = dividir(10.0, 2.0);
let resultado2 = dividir(5.0, 0.0);
if resultado1.es_ok() {
println!("La operación fue exitosa");
}
// Obtener el valor o un valor por defecto
let valor1 = resultado1.valor_o_defecto(0.0);
let valor2 = resultado2.valor_o_defecto(0.0);
println!("Resultado 1: {}", valor1);
println!("Resultado 2: {}", valor2);
}
Este patrón es similar al que utilizan los tipos Option
y Result
predefinidos en Rust.
Implementando comportamiento para máquinas de estado
Los enums son ideales para modelar máquinas de estado, y los métodos nos permiten definir las transiciones entre estados:
enum EstadoPedido {
Recibido { timestamp: u64 },
EnProceso { inicio_proceso: u64, operador_id: u32 },
Enviado { codigo_seguimiento: String },
Entregado { fecha_entrega: u64 },
Cancelado { motivo: String },
}
impl EstadoPedido {
fn nuevo(timestamp: u64) -> Self {
EstadoPedido::Recibido { timestamp }
}
fn procesar(&self, operador_id: u32) -> EstadoPedido {
match self {
EstadoPedido::Recibido { timestamp } => {
EstadoPedido::EnProceso {
inicio_proceso: *timestamp,
operador_id
}
},
_ => {
println!("No se puede procesar un pedido que no está en estado 'Recibido'");
self.clonar()
}
}
}
fn enviar(&self, codigo: &str) -> EstadoPedido {
match self {
EstadoPedido::EnProceso { .. } => {
EstadoPedido::Enviado {
codigo_seguimiento: codigo.to_string()
}
},
_ => {
println!("Solo se pueden enviar pedidos en proceso");
self.clonar()
}
}
}
fn entregar(&self, fecha: u64) -> EstadoPedido {
match self {
EstadoPedido::Enviado { .. } => {
EstadoPedido::Entregado { fecha_entrega: fecha }
},
_ => {
println!("Solo se pueden entregar pedidos enviados");
self.clonar()
}
}
}
fn cancelar(&self, motivo: &str) -> EstadoPedido {
match self {
EstadoPedido::Entregado { .. } => {
println!("No se puede cancelar un pedido ya entregado");
self.clonar()
},
_ => EstadoPedido::Cancelado { motivo: motivo.to_string() }
}
}
// Método auxiliar para clonar el estado actual
fn clonar(&self) -> EstadoPedido {
match self {
EstadoPedido::Recibido { timestamp } =>
EstadoPedido::Recibido { timestamp: *timestamp },
EstadoPedido::EnProceso { inicio_proceso, operador_id } =>
EstadoPedido::EnProceso { inicio_proceso: *inicio_proceso, operador_id: *operador_id },
EstadoPedido::Enviado { codigo_seguimiento } =>
EstadoPedido::Enviado { codigo_seguimiento: codigo_seguimiento.clone() },
EstadoPedido::Entregado { fecha_entrega } =>
EstadoPedido::Entregado { fecha_entrega: *fecha_entrega },
EstadoPedido::Cancelado { motivo } =>
EstadoPedido::Cancelado { motivo: motivo.clone() },
}
}
}
fn main() {
// Simulamos el ciclo de vida de un pedido
let mut pedido = EstadoPedido::nuevo(1634567890);
// Transiciones válidas
pedido = pedido.procesar(42);
pedido = pedido.enviar("TRACK123456");
pedido = pedido.entregar(1634657890);
// Transición inválida (no debería cambiar el estado)
pedido = pedido.cancelar("Ya no lo quiero");
}
En este ejemplo, cada método representa una transición válida entre estados y verifica que la transición sea permitida según el estado actual.
Métodos genéricos en enums
Los enums también pueden ser genéricos y tener métodos que trabajen con esos tipos genéricos:
enum Contenedor<T> {
Lleno(T),
Vacio,
}
impl<T> Contenedor<T> {
fn nuevo() -> Self {
Contenedor::Vacio
}
fn insertar(&self, valor: T) -> Self {
Contenedor::Lleno(valor)
}
fn extraer(&self) -> Option<&T> {
match self {
Contenedor::Lleno(valor) => Some(valor),
Contenedor::Vacio => None,
}
}
fn esta_vacio(&self) -> bool {
match self {
Contenedor::Vacio => true,
Contenedor::Lleno(_) => false,
}
}
}
fn main() {
let contenedor: Contenedor<i32> = Contenedor::nuevo();
println!("¿Está vacío? {}", contenedor.esta_vacio());
let contenedor = contenedor.insertar(42);
println!("¿Está vacío? {}", contenedor.esta_vacio());
if let Some(valor) = contenedor.extraer() {
println!("Valor extraído: {}", valor);
}
}
Este patrón es similar a cómo funciona el tipo Option<T>
en la biblioteca estándar de Rust.
Combinando métodos con pattern matching
Los métodos en enums se combinan perfectamente con el pattern matching para crear APIs expresivas:
enum Operacion {
Suma(i32, i32),
Resta(i32, i32),
Multiplicacion(i32, i32),
Division(i32, i32),
}
impl Operacion {
fn ejecutar(&self) -> Option<i32> {
match self {
Operacion::Suma(a, b) => Some(a + b),
Operacion::Resta(a, b) => Some(a - b),
Operacion::Multiplicacion(a, b) => Some(a * b),
Operacion::Division(a, b) => {
if *b != 0 {
Some(a / b)
} else {
None
}
}
}
}
fn describir(&self) -> String {
match self {
Operacion::Suma(a, b) => format!("{} + {}", a, b),
Operacion::Resta(a, b) => format!("{} - {}", a, b),
Operacion::Multiplicacion(a, b) => format!("{} * {}", a, b),
Operacion::Division(a, b) => format!("{} / {}", a, b),
}
}
}
fn main() {
let operaciones = vec![
Operacion::Suma(5, 3),
Operacion::Resta(10, 4),
Operacion::Multiplicacion(3, 6),
Operacion::Division(8, 2),
Operacion::Division(5, 0),
];
for op in operaciones {
match op.ejecutar() {
Some(resultado) => println!("{} = {}", op.describir(), resultado),
None => println!("{} = Error (división por cero)", op.describir()),
}
}
}
La combinación de enums con métodos y pattern matching crea un código que es a la vez seguro y expresivo, permitiéndonos modelar dominios complejos de manera elegante.
Los métodos en enums son una característica fundamental que complementa el sistema de tipos de Rust, permitiéndonos no solo definir qué datos pueden existir (a través de las variantes del enum), sino también qué comportamiento tienen esos datos. Esta combinación hace que los enums en Rust sean mucho más potentes que en la mayoría de los otros lenguajes de programación.
Otras lecciones de Rust
Accede a todas las lecciones de Rust y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Rust
Introducción Y Entorno
Primer Programa
Introducción Y Entorno
Instalación Del Entorno
Introducción Y Entorno
Funciones
Sintaxis
Operadores
Sintaxis
Estructuras De Control Condicional
Sintaxis
Arrays Y Strings
Sintaxis
Manejo De Errores Panic
Sintaxis
Variables Y Tipos Básicos
Sintaxis
Estructuras De Control Iterativo
Sintaxis
Colecciones Estándar
Estructuras De Datos
Option Y Result
Estructuras De Datos
Pattern Matching
Estructuras De Datos
Estructuras (Structs)
Estructuras De Datos
Enumeraciones Enums
Estructuras De Datos
El Concepto De Ownership
Ownership
Lifetimes Básicos
Ownership
Slices Y Referencias Parciales
Ownership
References Y Borrowing
Ownership
Funciones Anónimas Closures
Abstracción
Traits De La Biblioteca Estándar
Abstracción
Traits
Abstracción
Generics
Abstracción
Channels Y Paso De Mensajes
Concurrencia
Memoria Compartida Segura
Concurrencia
Threads Y Sincronización Básica
Concurrencia
Introducción A Tokio
Asincronía
Fundamentos Asíncronos Y Futures
Asincronía
Async/await
Asincronía
Ejercicios de programación de Rust
Evalúa tus conocimientos de esta lección Enumeraciones enums con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la definición y uso básico de enums con variantes simples en Rust.
- Aprender a asociar datos a variantes de enums para modelar dominios complejos.
- Implementar métodos en enums para añadir comportamiento específico a las variantes.
- Utilizar patrones de coincidencia (match) y sintaxis if let para manejar enums de forma segura y expresiva.
- Aplicar enums para modelar máquinas de estado, estructuras recursivas y resultados de operaciones con manejo de errores.