Crear listas con Vec

RustBeginner
Practicar Ahora

Introducción

Una variable almacena un valor, pero muchos programas necesitan una lista ordenada cuya longitud pueda cambiar. Vec<T> de Rust es una secuencia ampliable: cada elemento tiene el tipo T, los valores conservan su orden y el vector es el propietario de sus elementos.

Creará una lista de pedidos de una cafetería, leerá una posición de forma segura, recorrerá cada pedido sin consumir la lista y actualizará cada cadena almacenada mediante referencias mutables.

Crear y ampliar un vector

En este paso, añadirá tres cadenas propias a un vector inicialmente vacío.

Acceda al proyecto preparado y abra su código fuente:

cd /home/labex/project/cafe-orders
nano src/main.rs

La declaración Vec<String> significa «un vector cuyos elementos son valores String propios». Esta es la misma notación con corchetes angulares que vio en Option<u32> para indicar el tipo contenido: en este caso, el tipo de elemento contenido es String. Más adelante aprenderá a definir este tipo de tipos reutilizables en el laboratorio sobre genéricos. Vec::new() crea el vector vacío. La vinculación es mut porque añadir elementos modifica el vector.

Reemplace el comentario del paso 1 por estas tres llamadas a push:

    orders.push(String::from("Latte"));
    orders.push(String::from("Tea"));
    orders.push(String::from("Cake"));

push añade un elemento al final y conserva el orden de inserción. Guarde el archivo con Ctrl+O, pulse Enter y salga con Ctrl+X. Compruebe el código y ejecútelo:

cargo check
cargo run --quiet

La salida debe ser:

Order count: 3
Orders: ["Latte", "Tea", "Cake"]

len() informa de que hay tres elementos. El marcador :? utiliza el formato de depuración de Rust para mostrar el vector completo, incluidas las comillas de sus cadenas.

Leer una posición de forma segura

En este paso, leerá la segunda posición del vector sin arriesgarse a provocar un error por acceso fuera de los límites.

Las posiciones de un vector empiezan en cero, por lo que el índice 1 corresponde al segundo pedido. El método get devuelve un Option<&String>: Some contiene una referencia compartida cuando la posición existe y None representa una posición inexistente. La referencia permite leer el elemento mientras el vector sigue siendo su propietario.

Abra el código fuente:

nano src/main.rs

Reemplace el comentario del paso 2 por lo siguiente:

    match orders.get(1) {
        Some(order) => println!("Second order: {order}"),
        None => println!("Second order: missing"),
    }

Esto lee el elemento sin mover su String fuera del vector. Guarde el archivo, salga del editor y ejecute:

cargo run --quiet

Debería aparecer una nueva línea final:

Second order: Tea

Esta línea demuestra que la posición 1 existe y que se conservó el orden de inserción.

Iterar con una referencia compartida

En este paso, recorrerá todos los pedidos y mantendrá el vector disponible después del recorrido.

Escribir &orders toma prestado el vector en lugar de moverlo al bucle for. Por tanto, cada valor del bucle es una referencia compartida a uno de los valores String almacenados.

Abra el código fuente:

nano src/main.rs

Reemplace el comentario del paso 3 con este bucle y la comprobación posterior:

    for order in &orders {
        println!("Queued: {order}");
    }
    println!("Stored after display: {}", orders.len());

Guarde el archivo, salga del editor y ejecute:

cargo run --quiet

Las nuevas líneas deben ser:

Queued: Latte
Queued: Tea
Queued: Cake
Stored after display: 3

El recuento final se puede utilizar después del bucle porque el bucle tomó prestado el vector y devolvió cada referencia compartida antes de continuar.

Actualizar mediante referencias mutables

En este paso, tomará prestado cada elemento del vector de forma mutable y lo actualizará directamente antes de ejecutar el bucle de visualización.

Solo puede haber una referencia mutable activa a un valor a la vez. Un bucle for sobre &mut orders cumple esta regla porque proporciona una referencia mutable a un elemento, la utiliza durante la iteración actual y después pasa al elemento siguiente.

Abra el código fuente:

nano src/main.rs

Reemplace el comentario del paso 4 por lo siguiente:

    for order in &mut orders {
        order.push_str(" - ready");
    }

push_str añade texto tomado en préstamo a cada String propia. Rust utiliza automáticamente la String mutable que hay detrás de cada referencia para realizar esta llamada al método.

Guarde el archivo, salga del editor y compruebe el código y ejecútelo:

cargo check
cargo run --quiet

La salida completa debe ser:

Order count: 3
Orders: ["Latte", "Tea", "Cake"]
Second order: Tea
Queued: Latte - ready
Queued: Tea - ready
Queued: Cake - ready
Stored after display: 3

Las líneas de la cola actualizada demuestran que todos los elementos cambiaron directamente. El recuento final demuestra que el vector sigue siendo el propietario de las mismas tres cadenas.

Resumen

Creó y amplió un Vec<String>, accedió de forma segura a una posición mediante get, iteró sin consumir el vector y actualizó cada elemento mediante referencias mutables.