Manejo de errores en Rust: result y panic #
En Rust, el manejo de errores es una parte fundamental del desarrollo seguro y confiable. A diferencia de otros lenguajes que pueden propagar errores en tiempo de ejecución sin previo aviso, Rust nos obliga a manejar los errores de forma explícita. En este artículo, exploraremos las dos principales estrategias para manejar errores en Rust: Result
y panic!
, con ejemplos prácticos basados en el mundo Pokémon.
Panic!
: terminación inmediata del programa
#
La macro panic!
se usa cuando un error es irrecuperable y el programa no puede continuar su ejecución. Al invocarla, Rust imprimirá un mensaje de error y abortará el programa.
Ejemplo: intentar capturar un pokémon sin poké balls #
fn capturar_elemento(pokeballs: u8) {
if pokeballs == 0 {
panic!("¡No tienes PokeBalls! No puedes capturar Pokémon.");
} else {
println!("Lanzas una Poké Ball... ¡Captura exitosa!");
}
}
fn main() {
let pokeballs = 0;
capturar_elemento(pokeballs);
}
Salida esperada:
thread 'main' panicked at '¡No tienes PokeBalls! No puedes capturar Pokémon.', src/main.rs:4:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
📌 Cuándo usar panic!
:
- Cuando un error indica un estado irrecuperable (ejemplo: corrupción de memoria).
- En código prototipado donde aún no se ha implementado un manejo de errores adecuado.
- En situaciones donde fallar rápido es preferible a continuar con un estado inconsistente.
Result<t, e>
: manejo seguro de errores
#
Rust proporciona el tipo Result<T, E>
para manejar errores de manera controlada sin detener el programa.
enum Result<T, E> {
Ok(T),
Err(E),
}
Esto permite que una función retorne un Ok(valor)
en caso de éxito o un Err(error)
en caso de fallo.
Ejemplo: verificar si un pokémon puede aprender un movimiento #
enum ErrorAprendizaje {
NoTieneNivelSuficiente,
MovimientoNoCompatible,
}
fn aprender_movimiento(nivel: u8, compatible: bool) -> Result<&'static str, ErrorAprendizaje> {
if nivel < 15 {
return Err(ErrorAprendizaje::NoTieneNivelSuficiente);
}
if !compatible {
return Err(ErrorAprendizaje::MovimientoNoCompatible);
}
Ok("¡Movimiento aprendido con éxito!")
}
fn main() {
let resultado = aprender_movimiento(10, true);
match resultado {
Ok(mensaje) => println!("{}", mensaje),
Err(ErrorAprendizaje::NoTieneNivelSuficiente) => println!("El Pokémon necesita subir de nivel."),
Err(ErrorAprendizaje::MovimientoNoCompatible) => println!("El Pokémon no puede aprender este movimiento."),
}
}
Salida esperada:
El Pokémon necesita subir de nivel.
📌 Cuándo usar Result<T, E>
:
- Para manejar fallos que pueden ser recuperables.
- Al interactuar con operaciones externas como archivos, redes o bases de datos.
- Cuando se desea diferenciar entre éxito (
Ok
) y error (Err
).
Propagación de errores con ?
#
El operador ?
permite propagar errores automáticamente sin necesidad de usar match
explícito.
Ejemplo: leer datos de un archivo con información de un pokémon #
use std::fs::File;
use std::io::{self, Read};
fn leer_elemento_desde_archivo(nombre: &str) -> Result<String, io::Error> {
let mut archivo = File::open(nombre)?;
let mut contenido = String::new();
archivo.read_to_string(&mut contenido)?;
Ok(contenido)
}
fn main() {
match leer_elemento_desde_archivo("pokedex.txt") {
Ok(datos) => println!("Datos del Pokémon: {}", datos),
Err(e) => println!("Error al leer el archivo: {}", e),
}
}
📌 Cuándo usar ?
:
- Cuando una función retorna
Result<T, E>
y queremos propagar errores sin escribirmatch
explícito. - Para hacer el código más limpio y legible.
Evitando unwrap()
y expect()
en producción
#
Métodos como .unwrap()
y .expect()
pueden causar panic!
si un Result
es Err
.
let archivo = File::open("pokedex.txt").unwrap(); // Panics si el archivo no existe
En producción, es mejor manejar errores correctamente con match
o ?
en lugar de unwrap()
.
let archivo = File::open("pokedex.txt").unwrap_or_else(|_| {
println!("No se pudo abrir el archivo, creando uno nuevo.");
File::create("pokedex.txt").expect("Error al crear el archivo")
});
Conclusión #
Rust proporciona mecanismos seguros y explícitos para manejar errores:
panic!
se usa para fallos críticos e irrecuperables.Result<T, E>
permite manejar errores de manera controlada.?
simplifica la propagación de errores.- Evitar
unwrap()
yexpect()
en código de producción ayuda a mejorar la estabilidad del programa.
Aplicar estas estrategias correctamente permite escribir código más seguro, confiable y resistente a fallos.