2.4 Un nombre para lo ausente
Esta traducción fue asistida por IA y revisada superficialmente. Si encuentras errores o algo no suena natural, escríbeme — lo agradezco.
Un mensajero que vuelve con las manos vacías es útil exactamente en la medida en que puede explicar qué no encontró. “No lo encontré” cierra la conversación. “No encontré esta cosa concreta” abre la siguiente. La mayoría de herramientas prefieren lo primero. Esto suele llamarse brevedad, aunque la palabra exacta es algo diferente.
Lo que necesitas
get devuelve Option<&Task>. En el capítulo 2.1 era la elección correcta: una tarea o
existe o no, no hay motivos para su ausencia - solo el hecho. Desde entonces tq ha
cambiado. Los errores ahora tienen tipos; las funciones comparten un lenguaje común: Result.
Option tiene dos limitaciones en el contexto actual.
La primera: ? no funciona con él. main devuelve Result<(), TqError>, e intentar
aplicar ? a un Option<&Task> produce un error del compilador: Option y Result son
tipos distintos.
La segunda: la ausencia es anónima. None dice “no.” No dice “no hay ninguna tarea con id
42.” Cuando un error viaja hacia arriba, el llamador pierde contexto: ¿qué tarea
concretamente no se encontró?
TqError puede llevar información. Añade una variante NotFound(u64) - contendrá el id
de la tarea. get debe devolver exactamente eso.
La construcción
Primero, añade la nueva variante de error:
#[derive(Debug)]
enum TqError {
EmptyTitle,
NotFound(u64),
}
Al imprimirse para depuración, esto tiene el aspecto NotFound(42) - específico e
inequívoco.
Ahora get. La versión anterior devolvía Option<&Task> a través de find. La nueva usa
.ok_or(...) - un método de Option que convierte None en Err(e) y Some(v) en
Ok(v):
fn get(tasks: &[Task], id: u64) -> Result<&Task, TqError> {
tasks.iter().find(|task| task.id == id).ok_or(TqError::NotFound(id))
}
find busca; .ok_or(...) envuelve la ausencia en un error con nombre que lleva el id.
En main, ambas llamadas a get usan match. Los patrones cambian de Some/None a
Ok/Err:
match get(&tasks, 1) {
Ok(task) => println!("found: #{}: {}", task.id, task.title),
Err(e) => println!("error: {:?}", e),
}
match get(&tasks, 99) {
Ok(task) => println!("#{}: {}", task.id, task.title),
Err(TqError::NotFound(id)) => println!("task {} not found", id),
Err(e) => println!("error: {:?}", e),
}
El segundo match desestructura NotFound(id) - extrae el id del error y lo usa
directamente.
El brazo Err(e) en este match es actualmente inalcanzable: get solo produce
NotFound, no tiene otros errores. Pero el compilador ve TqError como un todo y exige que
todas las variantes estén cubiertas. Este capturador general es un patrón aceptado en Rust:
cuando TqError crezca y aparezcan nuevas variantes, este brazo las capturará sin ningún
cambio en el código.
El resultado
cargo run ahora imprime:
task 99 not found
No “not found” - “task 99 not found”. El mensajero nombró lo que no encontró.
? ahora funciona con get. Una función que busca una tarea y quiere pasar el error hacia
arriba puede escribir get(&tasks, id)? - y NotFound(id) viajará al llamador sin un
match adicional.
Un nuevo test para este cambio - comprueba que el id se conserva en el error:
#[test]
fn get_carries_missing_id_in_error() {
let tasks: Vec<Task> = Vec::new();
assert!(matches!(get(&tasks, 99), Err(TqError::NotFound(99))));
}
matches!(expr, pattern) devuelve true si la expresión coincide con el patrón -
incluyendo valores anidados. La suite completa de tests está en tq/.
make ci pasa. get ya no guarda silencio sobre qué estaba buscando.
El código completo de
tqpara este capítulo está en2-honest/04-a-name-for-the-absent/.
Lore: Option y Result - dos formas de decir no
Option dice: un valor o está presente o está ausente. Sin detalles - solo el hecho. Úsalo
cuando la ausencia es un resultado normal que no requiere explicación. Vec::first() devuelve
Option<&T>: un vector vacío no es un error, solo un estado.
Result dice: una operación tuvo éxito o falló - y si falló, aquí está el motivo. Úsalo
cuando el llamador necesita saber la razón.
Una regla práctica: si None es un resultado normal de trabajo, usa Option. Si None
significa “algo salió mal”, usa Result.