Expresar alternativas con enums y match

RustBeginner
Practicar Ahora

Introducción

Una estructura agrupa campos que siempre pertenecen juntos. Un enum modela un valor que puede ser una de varias alternativas, y cada alternativa puede transportar datos diferentes. La expresión match de Rust permite gestionar explícitamente esas alternativas.

Usted completará un enum de mensajes, desestructurará las coordenadas de una variante y ampliará el modelo con una nueva variante. El compilador mostrará deliberadamente por qué debe cubrir todas las variantes posibles antes de que pueda ejecutarse el procesador de mensajes.

Definir variantes que transportan datos

En este paso, definirá tres formas alternativas para un mismo tipo Message.

Entre en el proyecto preparado y abra el código fuente:

cd /home/labex/project/message-processor
nano src/main.rs

Una declaración de enum enumera las variantes entre llaves. Para construir una variante, se escribe el nombre del enum, :: y el nombre de la variante. Las distintas variantes de un mismo enum pueden transportar diferentes tipos de datos.

Reemplace el comentario del paso 1 por:

    Text(String),
    Move { x: i32, y: i32 },
    Stop,

Text(String) transporta un valor sin nombre, una forma que Rust denomina variante similar a una tupla. Move { x, y } transporta campos con nombre, por lo que se denomina variante similar a una estructura. Stop no transporta datos adicionales y se denomina variante unitaria. Al principio, es más importante recordar «un valor sin nombre, campos con nombre o ningún dato» que memorizar inmediatamente los nombres formales. Aunque sus formas son diferentes, cada valor construido tiene el único tipo Message.

Guarde con Ctrl+O, pulse Enter y salga con Ctrl+X. Compruebe y ejecute el procesador preparado:

cargo check
cargo run --quiet

La salida debe ser:

Text: Maintenance at 9
Move received
Stop requested

El match preparado selecciona un brazo para cada variante. Cada brazo tiene la forma pattern => behavior, y las comas separan los brazos cortos. El patrón Text(text) asigna un nombre a la cadena transportada. El brazo Move también asigna nombres a x y y, y después los agrupa temporalmente como (x, y). Los paréntesis crean aquí una tupla de dos valores que solo se utiliza como estructura auxiliar preparada; las tuplas no son un objetivo de aprendizaje de este laboratorio. El nombre _coordinates comienza por _, lo que indica a Rust que dejar este valor temporal sin usar es intencional. Así, la primera ejecución no muestra advertencias. En el siguiente paso, reemplazará esta estructura auxiliar y utilizará ambas coordenadas en la salida visible.

Usar los datos desestructurados de una variante

En este paso, cambiará el brazo Move de match para que las coordenadas extraídas por su patrón aparezcan en la salida.

Los patrones pueden descomponer la variante con la que coinciden. En una variante similar a una estructura, al escribir los nombres de los campos se crean enlaces que la expresión del brazo puede utilizar.

Abra el código fuente:

nano src/main.rs

Reemplace por completo el brazo Move por:

        Message::Move { x, y } => println!("Move to ({x}, {y})"),

Cuando llega un mensaje Move, el patrón enlaza sus campos almacenados con los nombres locales x y y. La expresión de impresión utiliza esos nombres; no hace falta acceder a los campos por separado ni realizar una conversión.

Guarde y salga, y después ejecute:

cargo run --quiet

La línea central debe ser ahora:

Move to (3, -2)

El valor negativo de y demuestra que el brazo utilizó los datos transportados por la variante real, en lugar de imprimir coordenadas fijas.

Ampliar un match exhaustivo

En este paso, añadirá un mensaje Pause, observará la comprobación de exhaustividad del compilador y después gestionará la nueva alternativa.

Un match debe cubrir todos los valores posibles de su enum. Esta regla hace que, al añadir una variante, se identifique cada match que necesita decidir qué hacer con el nuevo caso.

Abra el archivo y añada esta variante justo debajo de Stop:

nano src/main.rs
    Pause(u32),

Guarde y salga; después, compruebe el programa. Es normal que se produzca un error:

cargo check

Busque la parte estable del diagnóstico:

error[E0004]: non-exhaustive patterns: `Message::Pause(_)` not covered

El compilador señala match message porque el enum ahora puede contener un valor Pause, pero ningún brazo explica qué debe hacerse con él.

Abra de nuevo el código fuente. Añada este brazo debajo del brazo Stop:

        Message::Pause(seconds) => println!("Pause for {seconds} seconds"),

A continuación, elimine el // inicial de la línea preparada process(Message::Pause(5)); en main. El patrón asigna el nombre local seconds al valor u32 transportado.

Guarde y salga; después, compruebe y ejecute:

cargo check
cargo run --quiet

La salida completa debe ser:

Text: Maintenance at 9
Move to (3, -2)
Stop requested
Pause for 5 seconds

La comprobación correcta demuestra que el match vuelve a ser exhaustivo, y la cuarta línea demuestra que el nuevo brazo desestructuró la duración de Pause.

Resumen

Definió variantes de enum unitarias, similares a una tupla y similares a una estructura; desestructuró datos transportados mediante patrones de match; y utilizó el error del compilador E0004 para ampliar exhaustivamente un match después de añadir una nueva alternativa.