Formatar Strings em Rust

Aprenda como formatar strings em Rust com format!(), println!(), write!(), padding, precisão decimal e argumentos nomeados. Exemplos completos.

Formatar Strings em Rust

Rust oferece um sistema de formatação poderoso e seguro em tempo de compilação. As macros format!(), println!() e write!() usam a mesma sintaxe e cobrem praticamente qualquer necessidade de formatação de texto.

Formatação básica com format!() e println!()

A macro format!() retorna uma String formatada. println!() imprime direto no terminal:

fn main() {
    let nome = "Maria";
    let idade = 28;

    // println! imprime no terminal
    println!("Nome: {}, Idade: {}", nome, idade);

    // format! retorna uma String
    let msg = format!("Olá, {}! Você tem {} anos.", nome, idade);
    println!("{}", msg);

    // Posição dos argumentos
    println!("{0} tem {1} anos. {0} programa em Rust.", nome, idade);

    // Argumentos nomeados
    println!(
        "{nome} está aprendendo {linguagem}",
        nome = "João",
        linguagem = "Rust"
    );

    // Variáveis capturadas (Rust 1.58+)
    let linguagem = "Rust";
    println!("{nome} programa em {linguagem}");
}

Saída:

Nome: Maria, Idade: 28
Olá, Maria! Você tem 28 anos.
Maria tem 28 anos. Maria programa em Rust.
João está aprendendo Rust
Maria programa em Rust

Padding e alinhamento

Controle a largura e o alinhamento do texto com especificadores de formato:

fn main() {
    // Largura mínima (alinhado à direita por padrão)
    println!("[{:>10}]", "Rust");    // direita
    println!("[{:<10}]", "Rust");    // esquerda
    println!("[{:^10}]", "Rust");    // centro

    // Caractere de preenchimento personalizado
    println!("[{:*>10}]", "Rust");   // preenche com *
    println!("[{:-<10}]", "Rust");   // preenche com -
    println!("[{:.^10}]", "Rust");   // preenche com .

    // Números com zeros à esquerda
    println!("Código: {:05}", 42);
    println!("Binário: {:08b}", 42);

    // Tabela formatada
    println!("{:<15} {:>8} {:>10}", "Produto", "Qtd", "Preço");
    println!("{:<15} {:>8} {:>10.2}", "Notebook", 3, 4599.90);
    println!("{:<15} {:>8} {:>10.2}", "Mouse", 15, 89.99);
    println!("{:<15} {:>8} {:>10.2}", "Teclado", 8, 249.50);
}

Saída:

[      Rust]
[Rust      ]
[   Rust   ]
[******Rust]
[Rust------]
[...Rust...]
Código: 00042
Binário: 00101010
Produto              Qtd      Preço
Notebook               3    4599.90
Mouse                 15      89.99
Teclado                8     249.50

Precisão decimal e formatos numéricos

Controle a precisão de números decimais e use diferentes bases numéricas:

fn main() {
    let pi = std::f64::consts::PI;

    // Precisão decimal
    println!("Pi padrão:   {}", pi);
    println!("2 decimais:  {:.2}", pi);
    println!("5 decimais:  {:.5}", pi);
    println!("0 decimais:  {:.0}", pi);

    // Notação científica
    println!("Científica:  {:e}", 1_500_000.0_f64);
    println!("Científica:  {:E}", 0.00015_f64);

    // Bases numéricas
    let valor = 255_u32;
    println!("Decimal:     {}", valor);
    println!("Binário:     {:b}", valor);
    println!("Octal:       {:o}", valor);
    println!("Hexadecimal: {:x}", valor);
    println!("Hex maiúsc:  {:X}", valor);

    // Com prefixo de base
    println!("Com prefixo: {:#b}", valor);
    println!("Com prefixo: {:#o}", valor);
    println!("Com prefixo: {:#x}", valor);

    // Largura dinâmica
    let largura = 20;
    println!("{:>largura$.2}", pi, largura = largura);
}

Saída:

Pi padrão:   3.141592653589793
2 decimais:  3.14
5 decimais:  3.14159
0 decimais:  3
Científica:  1.5e6
Científica:  1.5E-4
Decimal:     255
Binário:     11111111
Octal:       377
Hexadecimal: ff
Hex maiúsc:  FF
Com prefixo: 0b11111111
Com prefixo: 0o377
Com prefixo: 0xff
                3.14

Usando write!() e writeln!()

A macro write!() escreve em qualquer tipo que implemente std::fmt::Write (strings) ou std::io::Write (arquivos, buffers):

use std::fmt::Write as FmtWrite;
use std::io::Write as IoWrite;

fn main() {
    // write! em String
    let mut texto = String::new();
    write!(texto, "Soma: {}", 2 + 3).unwrap();
    writeln!(texto, " (calculado)").unwrap();
    writeln!(texto, "Produto: {}", 2 * 3).unwrap();
    println!("{}", texto);

    // write! em Vec<u8> (buffer de bytes)
    let mut buffer: Vec<u8> = Vec::new();
    writeln!(buffer, "Linha 1: {}", "dados").unwrap();
    writeln!(buffer, "Linha 2: {:.2}", 3.14159).unwrap();
    let resultado = String::from_utf8(buffer).unwrap();
    println!("{}", resultado);
}

Saída:

Soma: 5 (calculado)
Produto: 6

Linha 1: dados
Linha 2: 3.14

Implementando Display para tipos personalizados

Para usar seus tipos com format!(), implemente o trait std::fmt::Display:

use std::fmt;

struct Moeda {
    simbolo: &'static str,
    valor: f64,
}

impl fmt::Display for Moeda {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {:.2}", self.simbolo, self.valor)
    }
}

fn main() {
    let preco = Moeda { simbolo: "R$", valor: 1499.90 };
    println!("Preço: {}", preco);
    println!("Debug: {:?}", format!("{}", preco));

    // Funciona com format! também
    let mensagem = format!("Total: {}", preco);
    println!("{}", mensagem);
}

Saída:

Preço: R$ 1499.90
Debug: "R$ 1499.90"
Total: R$ 1499.90

Referência rápida de especificadores

EspecificadorDescriçãoExemplo
{}Display padrãoprintln!("{}", 42)
{:?}Debugprintln!("{:?}", vec![1,2])
{:#?}Debug bonitoprintln!("{:#?}", vec![1,2])
{:.2}2 decimaisprintln!("{:.2}", 3.14159)
{:>10}Alinhado à direitaprintln!("{:>10}", "Rust")
{:<10}Alinhado à esquerdaprintln!("{:<10}", "Rust")
{:^10}Centralizadoprintln!("{:^10}", "Rust")
{:05}Zeros à esquerdaprintln!("{:05}", 42)
{:b}Binárioprintln!("{:b}", 10)
{:x}Hexadecimalprintln!("{:x}", 255)

Veja também