Introducción: ¿por qué Rust no usa try/catch?
En la mayoría de los lenguajes de programación (JavaScript, Python, Java) el manejo de errores se basa en el mecanismo de excepciones y la construcción try/catch. Sin embargo, en Rust este enfoque está ausente. En su lugar, los desarrolladores utilizan un potente sistema de tipos que hace que los errores sean parte de la firma de la función. Esto no es una casualidad, sino una decisión arquitectónica bien pensada: Rust busca previsibilidad y seguridad en tiempo de compilación, excluyendo caídas ocultas del programa.
En este artículo analizaremos:
- Por qué Rust abandonó
try/catch; - Cómo funcionan las herramientas principales:
Option,Result,unwrap(),expect(); - Qué es el operador
?(signo de interrogación); - Cuándo usar
panic!y la macrotry!(obsoleta); - Prácticas modernas de manejo de errores en Rust 2024.
1. Fundamentos: Option y Result — los dos tipos principales
Tipo Option: cuando un valor puede estar ausente
Option<T> es una enumeración (enum) que representa ya sea un valor (Some(T)) o su ausencia (None). Es ideal para situaciones donde no ocurre un error en el sentido clásico, pero el resultado puede estar vacío.
fn encontrar_usuario(id: u32) -> Option<String> { let base = vec!["Alice".to_string(), "Bob".to_string()]; if id < base.len() { Some(base[id].clone()) } else { None }}
fn main() { match encontrar_usuario(1) { Some(nombre) => println!("Encontrado: {}", nombre), None => println!("Usuario no encontrado"), }}Tipo Result: éxito o error
Result<T, E> es la herramienta principal para el manejo de errores. También es un enum: Ok(T) (éxito) o Err(E) (error).
use std::fs::File;use std::io::Read;
fn leer_archivo(ruta: &str) -> Result<String, std::io::Error> { let mut archivo = File::open(ruta)?; let mut contenido = String::new(); archivo.read_to_string(&mut contenido)?; Ok(contenido)}2. Operador ? (signo de interrogación) — reemplazo de try/catch
El operador ? es la forma principal de propagar errores hacia arriba en Rust. Reemplaza las voluminosas construcciones match y hace que el código sea limpio. Si el resultado es Ok, el valor se extrae y se asigna a la variable. Si es Err, la función devuelve inmediatamente ese error.
use std::fs;use std::io;
fn copiar_archivo(origen: &str, destino: &str) -> io::Result<()> { let datos = fs::read(origen)?; // si hay error — salida fs::write(destino, &datos)?; // si hay error — salida Ok(())}
fn main() -> io::Result<()> { copiar_archivo("source.txt", "dest.txt")?; println!("Archivo copiado exitosamente"); Ok(())}Importante: el operador ? solo funciona en funciones que devuelven Result, Option o tipos que implementan el trait FromResidual.
3. panic!, unwrap y expect — cuando el error es fatal
Macro panic!
panic!() provoca la terminación abrupta del programa (stack unwinding). Se usa solo en situaciones donde continuar la ejecución es imposible o no tiene sentido: errores en la lógica del programa, violación de invariantes, pruebas.
fn division(a: i32, b: i32) -> i32 { if b == 0 { panic!("¡División por cero!"); } a / b}Métodos unwrap() y expect()
unwrap() es una forma rápida de obtener un valor de Option o Result. Si contiene None o Err — pro