fmt::Debug în Rust

Trait-ul pentru afișarea de depanare cu {:?} și {:#?}; se obține aproape mereu cu #[derive(Debug)].

Sintaxă
#[derive(Debug)]
struct Tip { ... }

println!("{:?}", valoare);    // pe un rând
println!("{:#?}", valoare);   // pe mai multe rânduri

Ce face fmt::Debug

Debug este formatul pentru programatori: arată structura valorii (numele tipului, câmpurile, ghilimelele textelor). Îl folosești cu {:?} în println! și format!, cu {:#?} pentru o afișare indentată, și cu dbg!. Tipurile din std (numere, String, Vec, Option, HashMap) îl au deja.

Pentru tipurile tale adaugi #[derive(Debug)]. Dacă vrei alt format (de exemplu să ascunzi o parolă), îl implementezi manual cu impl fmt::Debug, de obicei cu ajutorul lui f.debug_struct.

Atenție la avertismente: câmpurile folosite doar prin Debug sunt considerate „necitite” (field is never read), pentru că derivarea lui Debug este ignorată de analiza codului mort. Pentru afișarea către utilizatori există Display.

Ce returnează

Metoda fmt(&self, f: &mut Formatter) -> fmt::Result, apelată de macro-urile de formatare.

Exemple

Exemplul 1: derive(Debug), {:?} și {:#?}

#[derive(Debug)]
enum Statut {
    Activ,
    Suspendat { motiv: String },
}

#[derive(Debug)]
struct Client {
    nume: String,
    oras: &'static str,
    statut: Statut,
}

fn main() {
    let c = Client {
        nume: String::from("Ana Pop"),
        oras: "Iași",
        statut: Statut::Suspendat { motiv: String::from("factură neplătită") },
    };
    println!("{c:?}");
    println!("{c:#?}");
    if let Statut::Suspendat { motiv } = &c.statut {
        println!("{} din {} este suspendat: {motiv}", c.nume, c.oras);
    }
    println!("{:?}", Statut::Activ);
}

Rezultat

Client { nume: "Ana Pop", oras: "Iași", statut: Suspendat { motiv: "factură neplătită" } }
Client {
    nume: "Ana Pop",
    oras: "Iași",
    statut: Suspendat {
        motiv: "factură neplătită",
    },
}
Ana Pop din Iași este suspendat: factură neplătită
Activ

Exemplul 2: Debug implementat manual

use std::fmt;

struct Temperatura {
    celsius: f64,
}

impl fmt::Debug for Temperatura {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Temperatura({:.1}°C)", self.celsius)
    }
}

fn main() {
    let t = vec![Temperatura { celsius: 21.456 }, Temperatura { celsius: -3.0 }];
    println!("{t:?}");
}

Rezultat

[Temperatura(21.5°C), Temperatura(-3.0°C)]

Greșeli frecvente

  • Afișezi un tip propriu cu {:?} fără #[derive(Debug)]: `Client` doesn't implement `Debug`.
  • Folosești {:?} pentru mesaje către utilizator: textele apar între ghilimele și cu caractere escapate. Implementează Display și folosește {}.

Comenzi înrudite

  • fmt::Display: Trait-ul pentru afișarea către utilizator cu {}; se implementează manual și aduce automat to_string().
  • fmt::Formatter::debug_struct: Ajută la scrierea manuală a lui Debug în formatul standard Nume { camp: valoare }, inclusiv forma {:#?}.
  • fmt::Formatter: Obiectul primit de fmt() în Display și Debug: destinația textului plus opțiunile din {:…} (lățime, precizie, #).
  • Clone: Trait-ul pentru copierea explicită a unei valori cu .clone(); se obține cu #[derive(Clone)] sau implementare manuală.