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ón | Qué 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
tqpara este capítulo está en1-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.