Manejo de errores en Rust: try/catch y más — nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Manejo de errores en Rust: try/catch y más — nivel avanzado

\n\n

A diferencia de muchos lenguajes donde las excepciones son el mecanismo estándar (como try/catch en Python, Java o C++), Rust ofrece un enfoque fundamentalmente diferente. Aquí no hay excepciones en el sentido clásico. En su lugar, el lenguaje utiliza un potente sistema de tipos para representar y manejar errores. Para un principiante esto puede parecer complejo, pero en un nivel avanzado apreciarás la seguridad y previsibilidad que ofrece este enfoque. En este artículo, analizaremos cómo trabajar con errores en Rust de manera profesional: desde los básicos Result y Option hasta los avanzados crates anyhow y thiserror, además de establecer paralelismos con el conocido try/catch.

\n\n

1. Fundamento: Result, Option y el operador ?

\n\n

La base del manejo de errores en Rust es la enumeración Result<T, E>. Puede ser Ok(T) (éxito) o Err(E) (error). Un análogo de try/catch sería el operador ?. Este simplifica la propagación del error hacia arriba: si el valor es Err, la función lo retorna inmediatamente; si es Ok, extrae el valor.

\n\n
use std::fs::File;\nuse std::io::Read;\n\nfn read_file(path: &str) -> Result<String, std::io::Error> {\n    let mut file = File::open(path)?; // Análogo a try: propagación del error\n    let mut content = String::new();\n    file.read_to_string(&mut content)?;\n    Ok(content)\n}
\n\n

En este ejemplo, File::open y read_to_string pueden devolver Err. El operador ? reemplaza el engorroso match y hace que el código sea limpio, como try/catch en otros lenguajes. Sin embargo, a diferencia de las excepciones, indicas explícitamente el tipo de error en la firma de la función, lo que mejora la legibilidad y la seguridad.

\n\n

La enumeración Option<T> se utiliza para casos donde un valor puede estar ausente (análogo a None en Python). El operador ? también funciona con ella:

\n\n
fn get_first_element(vec: &Vec<i32>) -> Option<&i32> {\n    vec.first()? // Si el vector está vacío, retornamos None\n}
\n\n

2. Trabajo avanzado con tipos de error: traits From y Error

\n\n

Cuando una función llama a varias operaciones con diferentes tipos de error (por ejemplo, entrada/salida y análisis sintáctico), surge un problema: ¿cómo devolver un tipo único? La solución son el trait From y un tipo de error personalizado.

\n\n

Creemos nuestra propia estructura de error e implementemos para ella el trait std::error::Error (automáticamente mediante #[derive(Debug)] e implementación manual de Display):

\n\n
use std::fmt;\nuse std::io;\n\n#[derive(Debug)]\nenum MyError {\n    Io(io::Error),\n    Parse(std::num::ParseIntError),\n}\n\nimpl fmt::Display for MyError {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        match self {\n            MyError::Io(e) => write!(f, "Error de E/S: {}", e),\n            MyError::Parse(e) => write!(f, "Error de análisis: {}", e),\n        }\n    }\n}\n\nimpl std::error::Error for MyError {}\n\n// Implementamos From para la conversión automática\nimpl From<io::Error> for MyError {\n    fn from(error: io::Error) -> Self {\n        MyError::Io(error)\n    }\n}\n\nimpl From<std::num::ParseIntError> for MyError {\n    fn from(error: std::num::ParseIntError) -> Self {\n        MyError::Parse(error)\n    }\n}\n\nfn process_data(path: &str) -> Result<i32, MyError> {\n    let content = std::fs::read_to_string(path)?; // Se convierte automáticamente a MyError::Io\n    let number: i32 = content.trim().parse()?;    // Se convierte automáticamente a MyError::Parse\n    Ok(number)\n}
\n\n

Ahora la función process_data devuelve un tipo único MyError, y el operador ? convierte automáticamente los errores gracias al trait From implementado.

Blogs

Book Recommendations