← Índice

1.10 La lista por partes

Esta traducción fue asistida por IA y revisada superficialmente. Si encuentras errores o algo no suena natural, escríbeme — lo agradezco.

Tomar del estante lo que se necesita en lugar de toda la fila no es una habilidad - es lo obvio. Lo obvio tiene una cualidad desagradable: todos lo entienden excepto las herramientas. La lista de tareas no es una excepción - solo puede verse completa.

El intento

add tiene nombre. El siguiente paso es claro: darle nombre también a la revisión. Por analogía con add - &Vec<Task> como entrada:

fn list(tasks: &Vec<Task>) {
    for task in tasks {
        println!("#{}: {} - {:?}", task.id, task.title, task.status);
    }
}

Una llamada con una tarea funciona:

add(&mut tasks, Task::new(1, "Buy coffee"));
list(&tasks);

Pero se necesita más: mostrar no toda la lista sino solo una parte. Tras añadir la segunda y tercera tarea - solo esas:

add(&mut tasks, Task::new(2, "Buy milk"));
add(&mut tasks, Task::new(3, "Buy eggs"));
list(&tasks[1..3]);

El compilador responde:

error[E0308]: mismatched types
  --> src/main.rs:XX:XX
   |
XX |     list(&tasks[1..3]);
   |     ---- ^^^^^^^^^^^^ expected `&Vec<Task>`, found `&[Task]`
   |     |
   |     arguments to this function are incorrect

Lo que sabe el compilador

&tasks[1..3] no es una referencia a un Vec. Es &[Task]: una referencia a una sección contigua de tareas, empezando en el índice 1 hasta el 3 (sin incluirlo). Un slice no sabe de dónde vienen los datos - de un Vec, un array, otro slice.

&Vec<Task> y &[Task] son tipos distintos. Una función con parámetro &Vec<Task> acepta solo una referencia a un Vec completo - &[Task] no encaja. El inverso funciona de otra forma: si una función toma &[Task], puede recibir tanto &tasks (la lista completa) como &tasks[1..3] (una parte). Rust convierte &Vec<Task> a &[Task] automáticamente en el lugar de llamada - es una regla incorporada del lenguaje.

Sintaxis de rangos:

ExpresiónQué toma
&tasks[1..3]índice 1 hasta el 3 (sin incluir)
&tasks[1..]índice 1 hasta el final
&tasks[..2]desde el inicio hasta el 2 (sin incluir)
&tasks[..]la lista completa

El conjuro

Un solo cambio - &Vec<Task> por &[Task]:

fn list(tasks: &[Task]) {
    for task in tasks {
        println!("#{}: {} - {:?}", task.id, task.title, task.status);
    }
}

Ahora list acepta tanto la lista completa como cualquier parte de ella. Dos llamadas en main:

fn main() {
    let mut tasks: Vec<Task> = Vec::new();

    add(&mut tasks, Task::new(1, "Buy coffee"));
    list(&tasks);

    add(&mut tasks, Task::new(2, "Buy milk"));
    add(&mut tasks, Task::new(3, "Buy eggs"));

    let task = &mut tasks[0];
    task.complete();

    if task.is_done() {
        println!("Task 1 done");
    }

    list(&tasks[1..]);
}
#1: Buy coffee - Todo
Task 1 done
#2: Buy milk - Todo
#3: Buy eggs - Todo

make ci pasa. list toma un slice - y ahora puede mostrar cualquier parte de la lista.

El código completo de tq para este capítulo está en 1-a-task/10-the-list-in-parts/.


Lore: Un slice y la lista completa

list(&tasks) también funciona - pasando la lista completa sin especificar un rango. &tasks tiene tipo &Vec<Task>, pero al pasarse a una función con parámetro &[Task], Rust lo convierte automáticamente.

En la práctica esto significa: las funciones que solo leen una lista se escriben mejor con &[Task]. Se vuelven más generales - funcionan con cualquier fuente de datos, no solo con un Vec.