Introducción: filosofía del manejo de errores en Rust
A diferencia de muchos lenguajes con excepciones (Java, Python, C++), Rust no utiliza la construcción clásica try/catch. En su lugar, el lenguaje ofrece un enfoque más explícito y type-safe. Los errores en Rust no son flujos de control ocultos, sino valores de retorno. Esto hace que el código sea predecible y protege contra fallos "silenciosos".
En Rust hay dos tipos principales para representar situaciones de error:
- Result<T, E> — para errores recuperables (errores de entrada/salida, parsing, lógica de negocio).
- Option<T> — para ausencia de valor (análogo a null, pero seguro).
En este artículo analizaremos cómo trabajar con estos tipos, cómo reemplazar el mundo de try/catch y cómo escribir código robusto en Rust.
1. Result<T, E>: la herramienta principal para el manejo de errores
El tipo Result es un enum con dos variantes: Ok(T) (éxito) y Err(E) (error). Se utiliza en la mayoría de las funciones que pueden fallar.
1.1. Creación y retorno de Result
use std::fs::File;use std::io::Read;
fn read_file(path: &str) -> Result<String, std::io::Error> { let mut file = File::open(path)?; // operador ? — análogo a try! let mut contents = String::new(); file.read_to_string(&mut contents)?; Ok(contents)}Aquí el operador ? es un elemento clave. Funciona como try en otros lenguajes: si el resultado es Err, la función retorna inmediatamente ese error. Si es Ok, extrae el valor.
1.2. Manejo con match
La forma más explícita es usar match:
let result = read_file("config.txt");
match result { Ok(content) => println!("Archivo leído: {}", content), Err(e) => eprintln!("Error: {}", e),}Esto es un análogo de try/catch, pero más flexible y controlable.
1.3. Métodos auxiliares
.unwrap()— entra en pánico ante un error (solo para prototipos)..expect(msg)— entra en pánico con un mensaje personalizado..unwrap_or(default)— retorna un valor por defecto..is_ok()/.is_err()— verificación de estado.
2. Option<T>: trabajo seguro con ausencia de valor
Option es otro enum con las variantes Some(T) y None. Reemplaza a null y nullptr, eliminando errores de desreferenciación de puntero nulo.
2.1. Ejemplo de uso
fn find_user(id: u32) -> Option<String> { if id == 1 { Some("Alice".to_string()) } else { None }}
let user = find_user(2);println!("Usuario: {}", user.unwrap_or("Invitado".to_string()));2.2. Combinación con Result
A menudo Option se convierte en Result para un manejo uniforme:
fn get_user_name(id: u32) -> Result<String, String> { find_user(id).ok_or("Usuario no encontrado".to_string())}El método .ok_or() convierte Some en Ok y None en Err.
3. Operador ?: elegante reemplazo de try/catch
El operador ? es azúcar sintáctico que desempaqueta automáticamente Result o Option. Solo está disponible en funciones que retornan el tipo correspondiente.
3.1. Ejemplo con cadena de llamadas
use std::num::ParseIntError;
fn parse_and_add(a: &str, b: &str) -> Result<i32, ParseIntError> { let x: i32 = a.parse()?; let y: i32 = b.parse()?; Ok(x