Depuración de código Rust: desde «println!» hasta herramientas profesionales
Cualquiera que haya escrito en Rust al menos una semana conoce esta sensación: el compilador lo dejó pasar, el programa se compiló, se ejecuta… y hace algo completamente inesperado. O simplemente falla con un misterioso Segmentation fault. ¡Bienvenido al mundo de la depuración! La buena noticia: en Rust hay todo un arsenal para esto, desde una macro simple hasta depuradores avanzados. En este artículo analizaremos todas las formas de depurar código Rust, desde las básicas hasta las profesionales.
1. Kit básico: macros dbg! y eprintln!
Empecemos con lo más simple y accesible. Si escribes código Rust y quieres entender rápidamente qué sucede con las variables, usa dbg!. Muestra tanto el nombre de la variable como su valor, y el archivo con la línea. Mucho más cómodo que el clásico println!.
fn calculate_even_sum(numbers: &[i32]) -> i32 { let mut sum = 0; for &n in numbers { if n % 2 == 0 { sum += n; dbg!(n, sum); // vemos cada número par y la suma actual } } sum}
fn main() { let data = vec![1, 2, 3, 4, 5, 6]; let result = calculate_even_sum(&data); dbg!(result);}Ejecútalo y verás algo como:
[src/main.rs:6:13] n = 2[src/main.rs:6:13] sum = 2[src/main.rs:6:13] n = 4[src/main.rs:6:13] sum = 6[src/main.rs:6:13] n = 6[src/main.rs:6:13] sum = 12[src/main.rs:12:5] result = 12Y si necesitas escribir en stderr (por ejemplo, para no interferir con stdout en tuberías), usa eprintln!. Esto es indispensable al depurar código Rust en entornos de producción.
2. Registro avanzado con log y env_logger
Cuando el proyecto crece, esparcir dbg! por todo el código se vuelve tedioso. Aquí vienen al rescate los crates log y env_logger. Permiten activar/desactivar registros sin recompilar.
use log::{info, warn, error, debug};
fn main() { env_logger::init();
info!("Programa iniciado"); let x = 42; debug!("Valor de x: {}", x);
if x > 100 { warn!("¡x es demasiado grande!"); } else { error!("Esto no es un error, solo una comprobación"); }}Ejecuta con la variable de entorno RUST_LOG=debug cargo run y obtendrás todos los registros. ¿Solo quieres advertencias? RUST_LOG=warn. Muy útil para depurar aplicaciones Rust en tiempo real.
3. Pánico y backtrace: leyendo la pila de errores
Cuando el programa entra en pánico, Rust muestra un mensaje. Pero a menudo esto no es suficiente. Activa el backtrace completo:
$ RUST_BACKTRACE=1 cargo runO para máxima detalle:
$ RUST_BACKTRACE=full cargo runVerás toda la pila de llamadas, desde el punto de fallo hasta main. Esto es críticamente importante al depurar código Rust si no entiendes de dónde proviene el error.
Ejemplo: supongamos que tienes este código:
fn main() { let v = vec![1, 2, 3]; println!("{}", v[10]); // ¡pánico!}Con RUST_BACKTRACE=1 no solo verás "index out of bounds", sino la línea exacta y la llamada.
4. Modo de depuración y optimización: debug vs release
Un punto importante: depurar código Rust en modo debug (por defecto) proporciona información completa. Pero en release, el compilador optimiza el código y algunas variables pueden desaparecer o cambiar de orden. Si estás depurando un error de "release", usa un perfil con información de depuración:
[profile.release]debug = trueEn Cargo.toml. Esto te permitirá ejecutar cargo run --release