Depuração de código Rust: do «println!» às ferramentas profissionais
Todo mundo que escreveu em Rust por pelo menos uma semana conhece essa sensação: o compilador aprovou, o programa compilou, é executado… e faz algo completamente inesperado. Ou simplesmente trava com um misterioso Segmentation fault. Bem-vindo ao mundo da depuração! A boa notícia: em Rust, há todo um arsenal para isso — desde uma macro simples até depuradores avançados. Neste artigo, vamos abordar todas as formas de depuração de código Rust, das básicas às profissionais.
1. Kit básico: macros dbg! e eprintln!
Vamos começar com o mais simples e acessível. Se você está escrevendo código Rust e quer entender rapidamente o que está acontecendo nas variáveis — use dbg!. Ele exibe o nome da variável, seu valor, e o arquivo com a linha. Muito mais prático que o velho 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 e a soma atual } } sum}
fn main() { let data = vec![1, 2, 3, 4, 5, 6]; let result = calculate_even_sum(&data); dbg!(result);}Execute — e verá 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 = 12E se precisar escrever no stderr (por exemplo, para não interferir no stdout durante pipes) — use eprintln!. Isso é indispensável na depuração de código Rust em ambiente de produção.
2. Logging avançado com log e env_logger
Quando o projeto cresce, espalhar dbg! por todo o código se torna doloroso. As crates log e env_logger vêm em socorro. Elas permitem ativar/desativar logs sem recompilação.
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 é muito grande!"); } else { error!("Isso não é um erro, apenas uma verificação"); }}Execute com a variável de ambiente RUST_LOG=debug cargo run — e obterá todos os logs. Quer apenas avisos? RUST_LOG=warn. Muito útil para depuração de aplicações Rust em tempo real.
3. Pânico e backtrace: lendo a pilha de erros
Quando o programa entra em pânico, o Rust exibe uma mensagem. Mas muitas vezes isso não é suficiente. Ative o backtrace completo:
$ RUST_BACKTRACE=1 cargo runOu para máxima detalhamento:
$ RUST_BACKTRACE=full cargo runVocê verá toda a pilha de chamadas — do ponto da falha até o main. Isso é criticamente importante na depuração de código Rust se você não entender de onde veio o erro.
Exemplo: suponha que você tenha este código:
fn main() { let v = vec![1, 2, 3]; println!("{}", v[10]); // pânico!}Com RUST_BACKTRACE=1, você verá não apenas "index out of bounds", mas a linha exata e a chamada.
4. Modo de depuração e otimização: debug vs release
Um ponto importante: a depuração de código Rust no modo debug (padrão) fornece informações completas. Mas no modo release, o compilador otimiza o código, e algumas variáveis podem desaparecer ou ter a ordem alterada. Se você está depurando um bug de "release" — use um perfil com informações de depuração:
[profile.release]debug = trueNo Cargo.toml. Isso permitirá executar cargo run --release