Introducción: ¿por qué el iterador no quiere detenerse?
\n\nImagine: está escribiendo un programa en Rust que procesa una lista de números — analiza cadenas, lee archivos, realiza algunas transformaciones. Usa Iterator::map para recorrer cada elemento. Y de repente — ¡pum! — uno de los elementos se convierte en un error Result::Err. Sería lógico detener inmediatamente la iteración y devolver ese error hacia arriba. Pero map simplemente sigue funcionando como si nada. ¿Por qué? Y lo más importante: ¿cómo solucionarlo?
Esta pregunta es una de las más populares en Stack Overflow entre los principiantes en Rust. Y con razón. Los mecanismos de manejo de errores en Rust son flexibles, pero requieren un enfoque consciente. En este artículo, analizaremos el problema en profundidad, veremos varias soluciones y elegiremos la más elegante. ¡Allá vamos!
\n\nEl problema: map no se detiene
\n\nEmpecemos con un ejemplo clásico. Supongamos que tenemos un vector de cadenas que queremos convertir en números:
\n\nfn main() {\n let inputs = vec!["42", "hello", "100"];\n\n let result: Vec<Result<i32, std::num::ParseIntError>> =\n inputs.iter().map(|s| s.parse::<i32>()).collect();\n\n println!("{:?}", result);\n}\n\nEste código se compilará y ejecutará. Pero, ¿qué obtenemos? Un vector de Results: [Ok(42), Err(ParseIntError), Ok(100)]. La iteración no se detuvo en "hello", sino que continuó. Para muchas tareas, esto está bien — quiere recopilar todos los errores. Pero, ¿qué pasa si necesita la primera excepción? Por ejemplo, al cargar una configuración: si un parámetro es incorrecto, no tiene sentido seguir verificando.
El problema es que map es una transformación perezosa. No sabe qué es "detenerse". Simplemente dice: "Dame un elemento, aplicaré la función, devolveré el resultado". Todo el control de pausa y continuación recae en el consumidor del iterador (collect, for, fold, etc.). Y collect para Vec simplemente va hasta el final.
Solución 1: collect con FromIterator (la más popular)
\n\nLa respuesta más común en Stack Overflow es usar collect::<Result<Vec<_>, _>>(). Rust sabe que puede recopilar un iterador de Results en un solo Result con un vector dentro. Si encuentra un Err, la recopilación se detiene y se devuelve ese error.
fn main() -> Result<(), std::num::ParseIntError> {\n let inputs = vec!["42", "hello", "100"];\n\n let result: Vec<i32> =\n inputs.iter()\n .map(|s| s.parse::<i32>())\n .collect::<Result<Vec<_>, _>>()?; // <-- ¡magia!\n\n println!("{:?}", result);\n Ok(())\n}\n\n¿Cómo funciona? El tipo Result<Vec<i32>, ParseIntError> implementa el trait FromIterator. Cuando collect ve un Err, lo devuelve inmediatamente, sin procesar los elementos restantes. Si todo está bien, obtiene Ok(vector). Es breve, idiomático y eficiente.
Desventaja: pierde información sobre en qué elemento ocurrió el error (a menos que lo envuelva en su propio tipo). Pero para scripts rápidos, es perfecto.
\n\nSolución 2: filter_map para ignorar errores
\n\nA veces no quiere detenerse, sino simplemente ignorar los elementos fallidos. Por ejemplo, contar solo las cadenas válidas. Para esto, filter_map es ideal:
fn main() {\n let inputs = vec!["42", "hello", "100", "abc", "7"];\n\n let numbers: Vec<i32> = inputs.iter()\n .filter_map(|s| s.parse::<i32>().ok()) // .ok() convierte Err en None\n .collect();\n\n println!("{:?}", numbers); // [42, 100, 7]\n}\n\nfilter_map acepta