fmt::Write în Rust

Trait-ul care permite scrierea de text formatat cu write!/writeln! direct într-un String sau alt destinatar de text.

Sintaxă
use std::fmt::Write;

write!(s, "{} lei", pret)?;
writeln!(s, "linie")?;

pub trait Write {
    fn write_str(&mut self, s: &str) -> fmt::Result;
}

Ce face fmt::Write

std::fmt::Write este interfața pentru „locuri în care se poate scrie text”. String o implementează, deci după use std::fmt::Write; poți scrie write!(s, "{nume}: {nota}") și textul formatat se adaugă la final, fără un String intermediar, ca la s.push_str(&format!(...)).

write! returnează fmt::Result, care trebuie folosit: cu ? într-o funcție care returnează fmt::Result, sau cu .unwrap() (scrierea într-un String nu eșuează niciodată). Ca să faci propriul tip destinatar, implementezi o singură metodă, write_str; write_char și write_fmt vin automat.

Nu confunda cu std::io::Write, folosit pentru octeți (fișiere, stdout). Dacă ai nevoie de ambele în același fișier, importă unul cu alt nume: use std::fmt::Write as _;.

Parametri

Parametru Descriere
write_str(s) Singura metodă obligatorie: adaugă textul s la destinatar.

Ce returnează

fmt::Result (Result<(), fmt::Error>).

Exemple

Exemplul 1: Un raport construit cu write! și writeln!

use std::fmt::Write;

fn raport(note: &[(&str, f64)]) -> Result<String, std::fmt::Error> {
    let mut s = String::new();
    writeln!(s, "{:<8}{:>6}", "Elev", "Media")?;
    for (nume, media) in note {
        writeln!(s, "{nume:<8}{media:>6.2}")?;
    }
    write!(s, "Total elevi: {}", note.len())?;
    Ok(s)
}

fn main() {
    let text = raport(&[("Ana", 9.5), ("Radu", 8.25), ("Ioana", 10.0)]).unwrap();
    println!("{text}");
}

Rezultat

Elev     Media
Ana       9.50
Radu      8.25
Ioana    10.00
Total elevi: 3

Exemplul 2: Propriul destinatar: un numărător de caractere

Implementăm doar write_str. write! formatează textul și îl trimite în bucăți la metoda noastră.

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

struct Numarator {
    caractere: usize,
}

impl Write for Numarator {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.caractere += s.chars().count();
        Ok(())
    }
}

fn main() {
    let mut n = Numarator { caractere: 0 };
    write!(n, "Suma de plată: {:.2} lei", 1234.5).unwrap();
    n.write_char('!').unwrap();
    println!("s-ar fi scris {} caractere", n.caractere);
}

Rezultat

s-ar fi scris 27 caractere

Greșeli frecvente

  • Uiți use std::fmt::Write;: write! pe un String dă cannot write into `String`, cu sugestia de a importa trait-ul.
  • Ignori rezultatul lui write!: compilatorul avertizează unused `Result` that must be used.
  • Imporți și std::io::Write, și std::fmt::Write cu același nume: conflict. Folosește use std::fmt::Write as _;.

Comenzi înrudite

  • fmt::Formatter: Obiectul primit de fmt() în Display și Debug: destinația textului plus opțiunile din {:…} (lățime, precizie, #).
  • String::push_str: Adaugă la finalul unui String un text (push_str) sau un singur caracter (push).
  • fmt::Display: Trait-ul pentru afișarea către utilizator cu {}; se implementează manual și aduce automat to_string().