Esta traducción fue asistida por IA y revisada superficialmente. Si encuentras errores o algo no suena natural, escríbeme - lo agradezco.
Ya has abierto una caja precintada antes. Mirabas dentro, actuabas según lo que encontrabas, seguías adelante - y hacías bien. Pero haz eso en cada paso de una cadena de búsquedas, y las líneas dedicadas a comprobar cajas acabarán superando a las que hacen el encargo en sí.
La cinta
Imagina el mismo archivo - las cajas precintadas, el archivero que no te deja salir con una sin abrir. A los visitantes nuevos nunca les cuentan una cosa: hay una sala trasera donde las cajas no se abren nunca a mano. Una caja avanza por la cinta. Un empleado la escanea - el precinto no se rompe - y si dentro hay un libro, el empleado hace lo que toque en ese tramo de la línea y precinta una caja nueva con el resultado. Si el escáner encuentra una nota, el empleado no hace nada. La caja sigue su camino intacta, pasa de largo por todos los empleados que quedan por delante, hasta llegar a quien la espera al final de la línea.
El hábito que acabas de aprender
En cuanto aprendes a respetar el precinto, dan ganas de abrir cada caja en el momento en que la
recibes. Encontraste el libro - ábrela con un match. Obtuviste el código de estante - ábrela
otra vez. Extrajiste el número de hueco del código - ábrela una tercera vez. Cada match es
correcto por sí solo. Tres seguidos convierten un encargo de tres pasos en nueve líneas, seis de
las cuales solo pasan un valor adelante o devuelven la misma queja que ya estaba escrita en la
caja.
struct Book {
id: u32,
shelf_code: Option<String>, // p. ej. "SHELF-104"
}
fn find_book(id: u32, archive: &[Book]) -> Option<&Book> {
archive.iter().find(|book| book.id == id)
}
fn parse_slot(raw: &str) -> Result<u32, String> {
raw.parse().map_err(|_| format!("bad shelf slot: {raw}"))
}
fn shelf_slot(id: u32, archive: &[Book]) -> Result<u32, String> {
let book = match find_book(id, archive) {
Some(b) => b,
None => return Err("no book with that id".to_string()),
};
let code = match &book.shelf_code {
Some(c) => c,
None => return Err("book has no shelf code yet".to_string()),
};
let raw = code.trim_start_matches("SHELF-");
match parse_slot(raw) {
Ok(slot) => Ok(slot),
Err(e) => Err(e), // la abre una tercera vez, solo para devolver lo que ya había encontrado
}
}
Por la cinta
La línea es la ruta principal del archivo. Por ella pasa casi cualquier caja del edificio; solo abres una a mano en el momento en que la decisión es de verdad tuya.
Rust llama map al empleado que solo trabaja con el libro y nunca lo convierte en una nota. Al
empleado que toma un libro y puede devolver su propia nota - porque su parte del trabajo puede
fallar - lo llama and_then. Cuando la decisión es realmente tuya, recurres a ? y miras
dentro de la caja en persona. Si hay un libro, lo sacas y lo llevas tú mismo hasta el inicio del
siguiente tramo. Si hay una nota, no esperas a que la cinta llegue al final: te bajas aquí mismo
y llevas la nota a quien te mandó a hacer el encargo.
Pon a los empleados en fila y las decisiones manuales dejan de darse en cada paso - solo ocurren donde tú mismo trazas la línea.
fn shelf_slot(id: u32, archive: &[Book]) -> Result<u32, String> {
let raw = find_book(id, archive)
// sigue en la cinta solo si hay un libro y tiene código de estante
.and_then(|book| book.shelf_code.as_deref())
// siempre tiene éxito - nunca convierte un libro en una nota
.map(|code| code.trim_start_matches("SHELF-"))
// una nota aquí termina el encargo
.ok_or_else(|| "book has no shelf code yet".to_string())?;
parse_slot(raw)
}
Qué cambia
Dejas de abrir cada Option y Result sobre la marcha. La cadena comprueba exactamente lo
mismo que comprobaba la cadena de match - nada se da por hecho, la regla del archivo no se
dobla - solo que la comprobación ocurre en la línea, un empleado por paso, y tú recibes el
resultado una sola vez, al final, cuando de verdad toca decidir. Dos motivos por los que el
encargo podía volver vacío - no hay libro, no hay código de estante - se funden en un solo
mensaje por el camino; si más adelante necesitas distinguirlos, para eso está un ? aparte en
cada paso.
map- si la caja contiene un libro, lo transforma; si contiene una nota, la deja pasar intactaand_then- entrega el libro al siguiente empleado, cuyo escáner también puede devolver una nota?- miras dentro de la caja tú mismo; si hay una nota, la llevas directamente a quien te mandó a hacer el encargo
Una cosa más que vale la pena saber: en la línea trabajan otros empleados - filter mira
dentro del libro y decide si sirve, or_else pone su propio libro en lugar de una nota que
llega, y así sucesivamente. Los dos empleados de este artículo no son todo el taller - solo el
mínimo con el que conviene empezar a leer el código de otra persona.