format_args! în Rust

Pregătește un text formatat fără să-l construiască: produce un fmt::Arguments care poate fi scris oriunde, fără alocare.

Sintaxă
let args: std::fmt::Arguments = format_args!("{} {}", a, b);
fn log(args: std::fmt::Arguments) { ... }
log(format_args!("...", ...));

Ce face format_args!

format_args! este temelia tuturor macro-urilor de formatare: println!, format! și write! îl apelează intern. El verifică șablonul la compilare și produce o valoare std::fmt::Arguments, care ține șablonul și referințe la argumente. Textul propriu-zis nu este construit și nu se alocă memorie până când cineva nu scrie valoarea undeva.

Îl folosești când scrii propriile funcții de jurnalizare sau afișare: funcția primește fmt::Arguments, iar apelantul îi dă format_args!(...). Valoarea Arguments implementează Display, deci poate fi afișată cu {} sau scrisă cu write_fmt.

Rezultatul lui format_args! împrumută argumentele, deci de obicei se folosește imediat, în aceeași expresie (de exemplu ca argument de funcție), nu se păstrează într-o variabilă pentru mai târziu. Dacă șablonul nu are argumente, as_str() returnează textul ca &'static str; altfel returnează None.

Exemple

Exemplul 1: O funcție de jurnal care primește fmt::Arguments

jurnal adaugă nivelul în față. Macro-ul info! transmite șablonul și argumentele mai departe prin format_args!, deci se scrie la fel de comod ca println!.

use std::fmt;

fn jurnal(nivel: &str, mesaj: fmt::Arguments) {
    println!("[{nivel:<5}] {mesaj}");
}

macro_rules! info {
    ($($arg:tt)*) => { jurnal("INFO", format_args!($($arg)*)) };
}

fn main() {
    let utilizator = "ana";
    info!("{utilizator} s-a autentificat");
    info!("comanda {} are {} produse", 1042, 3);
    jurnal("EROARE", format_args!("plata pentru comanda {} a eșuat", 1043));
}

Rezultat

[INFO ] ana s-a autentificat
[INFO ] comanda 1042 are 3 produse
[EROARE] plata pentru comanda 1043 a eșuat

Exemplul 2: Scriere fără String intermediar și as_str

write_fmt scrie direct valoarea Arguments în destinație. as_str() dă textul când șablonul nu are argumente de formatat și None când are.

use std::io::{self, Write};

fn main() -> io::Result<()> {
    let mut iesire = io::stdout().lock();
    let pret = 49.9;
    iesire.write_fmt(format_args!("Preț: {pret:.2} lei\n"))?;

    let n = 5;
    println!("{:?}", format_args!("text fix").as_str());
    println!("{:?}", format_args!("cu argument {}", n).as_str());
    Ok(())
}

Rezultat

Preț: 49.90 lei
Some("text fix")
None

Greșeli frecvente

  • Salvezi rezultatul într-o variabilă cu argumente temporare (let a = format_args!("{}", x + 1);) și îl folosești mai târziu: eroare temporary value dropped while borrowed. Folosește-l direct, ca argument.
  • Folosești format! în funcțiile de jurnal care, de cele mai multe ori, nu afișează nimic (de exemplu mesaje de depanare dezactivate). Cu fmt::Arguments textul se construiește doar dacă este chiar scris.

Comenzi înrudite

  • format! (și sintaxa de formatare): Construiește un String nou din șablon și valori, cu aceeași sintaxă ca println!, dar fără să afișeze nimic.
  • write! și writeln!: Scriu text formatat într-o destinație aleasă: un String, un fișier, stdout sau Formatter-ul din Display. Returnează un Result.
  • println! și print!: Afișează text pe ieșirea standard: println! adaugă la final un rând nou, print! nu. Acoladele {} sunt înlocuite cu valori.
  • macro_rules!: Definește un macro propriu: cod care scrie cod la compilare, după tipare. Așa sunt construite println!, vec! sau assert_eq!.