2.1 La tarea ausente
Esta traducción fue asistida por IA y revisada superficialmente. Si encuentras errores o algo no suena natural, escríbeme — lo agradezco.
Un resultado negativo sigue siendo un resultado - como todo el mundo sabe. Es fácil olvidarlo, sobre todo si eres una herramienta. Una herramienta que no puede informar de ello vuelca el banco de trabajo. Desde fuera, la diferencia entre “no encontrado” y “roto” es obvia. Desde dentro - en absoluto.
Lo que necesitas
tq puede añadir tareas, recorrerlas y marcarlas como completadas. Todavía no puede
encontrar una tarea específica por su identificador. Se puede acceder a la lista directamente:
tasks[0], tasks[1]. Pero eso es un índice, no un id - y si la entrada pedida no está
ahí, la lista vuelca el banco de trabajo.
Crea una función get: recibe una lista y un identificador, y devuelve la tarea - o informa
honestamente de que no está.
La construcción
Rust tiene un tipo para esto: Option<T> - dos variantes:
Some(value)- el valor está presenteNone- el valor está ausente
Option<&Task> y &Task son tipos distintos. Para llegar a la tarea dentro, ambos casos
deben manejarse explícitamente. Con este tipo de retorno, ignorar accidentalmente la ausencia
no es posible.
fn get(tasks: &[Task], id: u64) -> Option<&Task> {
for task in tasks {
if task.id == id {
return Some(task);
}
}
None
}
La llamada pasa por match - cada variante recibe su propio camino:
match get(&tasks, 1) {
Some(task) => println!("#{}: {}", task.id, task.title),
None => println!("not found"),
}
Dos tests - dos caminos:
#[test]
fn get_finds_existing_task() {
let mut tasks = Vec::new();
add(&mut tasks, Task::new(1, "Buy coffee"));
assert!(get(&tasks, 1).is_some());
}
#[test]
fn get_returns_none_for_absent_task() {
let tasks: Vec<Task> = Vec::new();
assert!(get(&tasks, 99).is_none());
}
La suite completa de tests está en tq/.
El resultado
El main de abajo muestra solo lo nuevo - dos llamadas a get. El código completo con
list y todo lo acumulado hasta ahora está en tq/.
fn main() {
let mut tasks: Vec<Task> = Vec::new();
add(&mut tasks, Task::new(1, "Buy coffee"));
add(&mut tasks, Task::new(2, "Buy milk"));
add(&mut tasks, Task::new(3, "Buy eggs"));
match get(&tasks, 1) {
Some(task) => println!("#{}: {}", task.id, task.title),
None => println!("not found"),
}
match get(&tasks, 99) {
Some(task) => println!("#{}: {}", task.id, task.title),
None => println!("not found"),
}
}
#1: Buy coffee
not found
make ci pasa. get devuelve una tarea o su ausencia - el código que llama decide qué
hacer con cada una.
El código completo de
tqpara este capítulo está en2-honest/01-the-absent-task/.
Lore: iter().find()
La función get implementa una búsqueda estándar: iterar, comprobar la condición, devolver
la primera coincidencia. Lo mismo puede escribirse con iter().find():
fn get(tasks: &[Task], id: u64) -> Option<&Task> {
tasks.iter().find(|task| task.id == id)
}
El Option es el mismo - find devuelve Some(&Task) si encuentra, None si no. La forma
expandida se usa en el texto para que Some y None sean visibles explícitamente. El código
en tq/ usa esta forma - no solo por razones estéticas: clippy conoce el lint manual_find
y rechaza el bucle expandido. make ci no pasará la versión del texto sin cambios; esta es
una divergencia intencionada entre la ilustración y el código funcional.
Lore: if let
match requiere manejar ambas variantes. Cuando solo se necesita una, if let es más corto:
if let Some(task) = get(&tasks, 1) {
println!("#{}: {}", task.id, task.title);
}
Si la tarea no se encuentra, no ocurre nada. Para los casos en que la ausencia no requiere
ninguna acción separada, esto es más preciso que un match con None => {} vacío.