O que é Fmt em Go?
O pacote fmt (abreviação de “format”) é um dos pacotes mais utilizados em Go. Ele implementa formatação de entrada e saída (I/O) com funções análogas a printf e scanf da linguagem C, mas com capacidades estendidas e segurança de tipos. Praticamente todo programa Go usa fmt — desde um simples “Hello, World!” até sistemas complexos de produção.
O pacote fmt é parte da biblioteca padrão de Go e trabalha em conjunto com outros pacotes fundamentais como io para escrita em streams, string para manipulação de texto, e error para criação de erros formatados.
Primeiro exemplo
package main
import "fmt"
func main() {
nome := "Go"
versao := 1.22
fmt.Printf("Bem-vindo ao %s versão %.2f!\n", nome, versao)
// Bem-vindo ao Go versão 1.22!
}
Funções de Impressão
O pacote fmt oferece três famílias de funções de impressão, cada uma com um destino diferente:
Print, Println, Printf — escrevem para stdout
fmt.Print("Sem newline") // imprime sem newline
fmt.Println("Com newline") // adiciona \n no final
fmt.Printf("Formatado: %d\n", 42) // formatação com verbos
Fprint, Fprintln, Fprintf — escrevem para io.Writer
Essas funções aceitam um io.Writer como primeiro argumento, permitindo escrever para qualquer destino — arquivos, buffers, conexões de rede, ou respostas HTTP:
// Escrevendo para arquivo
arquivo, _ := os.Create("saida.txt")
defer arquivo.Close()
fmt.Fprintln(arquivo, "Escrito no arquivo")
// Escrevendo para buffer
var buf bytes.Buffer
fmt.Fprintf(&buf, "Buffer contém: %d bytes", 42)
// Escrevendo em resposta HTTP
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Olá, %s!", r.URL.Query().Get("nome"))
}
Sprint, Sprintln, Sprintf — retornam string
Em vez de imprimir, retornam o resultado como uma string. São extremamente úteis para construir mensagens dinâmicas:
nome := "Maria"
idade := 30
mensagem := fmt.Sprintf("%s tem %d anos", nome, idade)
// mensagem = "Maria tem 30 anos"
erro := fmt.Sprintf("falha ao processar item %d de %d", atual, total)
Verbos de Formatação
Os verbos são o coração do pacote fmt. Cada verbo começa com % e indica como formatar o argumento correspondente:
Verbos gerais
type Usuario struct {
Nome string
Idade int
}
u := Usuario{"Ana", 25}
fmt.Printf("%v\n", u) // {Ana 25} — valor padrão
fmt.Printf("%+v\n", u) // {Nome:Ana Idade:25} — com nomes dos campos
fmt.Printf("%#v\n", u) // main.Usuario{Nome:"Ana", Idade:25} — sintaxe Go
fmt.Printf("%T\n", u) // main.Usuario — tipo
fmt.Printf("%%\n") // % — literal
Verbos para inteiros
n := 42
fmt.Printf("%d\n", n) // 42 — decimal
fmt.Printf("%b\n", n) // 101010 — binário
fmt.Printf("%o\n", n) // 52 — octal
fmt.Printf("%x\n", n) // 2a — hexadecimal minúsculo
fmt.Printf("%X\n", n) // 2A — hexadecimal maiúsculo
fmt.Printf("%c\n", 65) // A — caractere Unicode
fmt.Printf("%U\n", 'A') // U+0041 — formato Unicode
fmt.Printf("%05d\n", n) // 00042 — padding com zeros
Verbos para floats
pi := 3.14159265
fmt.Printf("%f\n", pi) // 3.141593 — decimal
fmt.Printf("%.2f\n", pi) // 3.14 — 2 casas decimais
fmt.Printf("%e\n", pi) // 3.141593e+00 — notação científica
fmt.Printf("%g\n", pi) // 3.14159265 — mais compacto
fmt.Printf("%10.2f\n", pi) // 3.14 — largura total 10
Verbos para strings e bytes
s := "Golang"
fmt.Printf("%s\n", s) // Golang — string pura
fmt.Printf("%q\n", s) // "Golang" — com aspas
fmt.Printf("%x\n", s) // 476f6c616e67 — hex dos bytes
fmt.Printf("%-10s|\n", s) // Golang | — alinhado à esquerda
fmt.Printf("%10s|\n", s) // Golang| — alinhado à direita
Verbos para ponteiros e booleanos
x := 42
fmt.Printf("%p\n", &x) // 0xc0000b4008 — endereço do ponteiro
fmt.Printf("%t\n", true) // true — booleano
Interface Stringer
A interface fmt.Stringer permite que seus tipos personalizem como são impressos pelo pacote fmt. Qualquer type que implemente o method String() string será formatado automaticamente quando usado com %v ou %s:
type Moeda struct {
Valor float64
Simbolo string
}
func (m Moeda) String() string {
return fmt.Sprintf("%s %.2f", m.Simbolo, m.Valor)
}
func main() {
preco := Moeda{Valor: 199.90, Simbolo: "R$"}
fmt.Println(preco) // R$ 199.90
fmt.Printf("Preço: %v\n", preco) // Preço: R$ 199.90
fmt.Printf("Preço: %s\n", preco) // Preço: R$ 199.90
}
Interface GoStringer
Para customizar a saída do verbo %#v (representação Go), implemente GoString() string:
func (m Moeda) GoString() string {
return fmt.Sprintf("Moeda{Valor: %.2f, Simbolo: %q}", m.Valor, m.Simbolo)
}
func main() {
preco := Moeda{Valor: 199.90, Simbolo: "R$"}
fmt.Printf("%#v\n", preco) // Moeda{Valor: 199.90, Simbolo: "R$"}
}
Fmt.Errorf e Wrapping de Erros
fmt.Errorf é a forma mais comum de criar errors formatados em Go. Com o verbo %w, introduzido no Go 1.13, você pode “embrulhar” (wrap) um erro existente, preservando a cadeia de erros para inspeção posterior:
func buscarUsuario(id int) (*Usuario, error) {
dados, err := bancoDeDados.Query("SELECT * FROM usuarios WHERE id = ?", id)
if err != nil {
return nil, fmt.Errorf("falha ao buscar usuário %d: %w", id, err)
}
usuario, err := decodificar(dados)
if err != nil {
return nil, fmt.Errorf("falha ao decodificar usuário %d: %w", id, err)
}
return usuario, nil
}
// O caller pode inspecionar a cadeia de erros
func main() {
u, err := buscarUsuario(123)
if err != nil {
// Verifica se o erro original é um erro de conexão
var connErr *net.OpError
if errors.As(err, &connErr) {
log.Println("Problema de conexão:", connErr)
}
// Verifica se é um erro específico
if errors.Is(err, sql.ErrNoRows) {
log.Println("Usuário não encontrado")
}
}
}
A diferença entre %v e %w:
%vformata o erro como texto, sem manter a referência ao erro original%wmantém a referência, permitindoerrors.Is()eerrors.As()na cadeia
Funções de Leitura (Scan)
O pacote fmt também oferece funções para ler entrada formatada, geralmente de os.Stdin:
func lerDados() {
var nome string
var idade int
// Scan — lê valores separados por espaço
fmt.Print("Nome e idade: ")
fmt.Scan(&nome, &idade)
// Scanf — lê com formato específico
var hora, minuto int
fmt.Print("Horário (HH:MM): ")
fmt.Scanf("%d:%d", &hora, &minuto)
// Scanln — lê até newline
var linha string
fmt.Print("Mensagem: ")
fmt.Scanln(&linha)
// Sscanf — lê de string
entrada := "42 3.14 true"
var n int
var f float64
var b bool
fmt.Sscanf(entrada, "%d %f %t", &n, &f, &b)
fmt.Printf("n=%d, f=%.2f, b=%t\n", n, f, b)
}
Para leitura de entrada mais robusta, considere usar bufio.Scanner, que lida melhor com linhas longas e diferentes delimitadores.
Performance e Boas Práticas
Evite fmt em hot paths
As funções do pacote fmt usam reflection internamente, o que tem custo de performance. Em caminhos críticos de performance, considere alternativas:
// LENTO — usa reflection
msg := fmt.Sprintf("usuário-%d", id)
// RÁPIDO — concatenação direta
msg := "usuário-" + strconv.Itoa(id)
// RÁPIDO — strings.Builder para múltiplas concatenações
var sb strings.Builder
sb.WriteString("usuário-")
sb.WriteString(strconv.Itoa(id))
sb.WriteString("-ativo")
msg := sb.String()
Resultados de benchmark típico:
BenchmarkSprintf 5000000 250 ns/op 48 B/op 2 allocs/op
BenchmarkConcat 20000000 80 ns/op 16 B/op 1 allocs/op
BenchmarkBuilder 30000000 50 ns/op 16 B/op 1 allocs/op
Evite fmt.Sprintf para erros simples
// Menos eficiente
return fmt.Errorf("valor inválido")
// Mais eficiente para erros sem formatação
return errors.New("valor inválido")
// Use Errorf apenas quando precisar de formatação ou %w
return fmt.Errorf("valor %d inválido para campo %s: %w", val, campo, err)
Tabela de referência rápida
| Verbo | Uso | Exemplo | Saída |
|---|---|---|---|
%v | Valor padrão | fmt.Sprintf("%v", 42) | 42 |
%+v | Struct com campos | fmt.Sprintf("%+v", s) | {Nome:Go} |
%#v | Sintaxe Go | fmt.Sprintf("%#v", s) | main.S{Nome:"Go"} |
%T | Tipo | fmt.Sprintf("%T", 42) | int |
%d | Decimal | fmt.Sprintf("%d", 42) | 42 |
%s | String | fmt.Sprintf("%s", "Go") | Go |
%q | String entre aspas | fmt.Sprintf("%q", "Go") | "Go" |
%w | Wrap error | fmt.Errorf(": %w", err) | erro encadeado |
Próximos Passos
Para continuar aprendendo sobre formatação, saída e I/O em Go:
- IO — interfaces Reader e Writer usadas por Fprint
- String — manipulação de strings em Go
- Error — sistema de erros e uso de Errorf com %w
- Log — logging com formatação similar a fmt
- Rune — caracteres Unicode e o verbo %c
- Type — tipos em Go e o verbo %T
- Interface — Stringer e GoStringer
- Go para Iniciantes — primeiros passos com Go
Perguntas Frequentes
Qual a diferença entre Print, Println e Printf?
fmt.Print imprime os argumentos sem newline e sem formatação. fmt.Println adiciona espaço entre argumentos e newline no final. fmt.Printf aceita um format string com verbos (como %d, %s) seguido dos argumentos correspondentes. Use Printf quando precisar de formatação precisa e Println para debug rápido.
Quando usar Sprintf vs concatenação de strings?
Use fmt.Sprintf para legibilidade quando a performance não é crítica — a formatação fica clara e fácil de manter. Use concatenação (+) ou strings.Builder em hot paths onde a performance importa, pois Sprintf usa reflection internamente. Em benchmark típicos, concatenação é 3-5x mais rápida que Sprintf.
Como funciona o verbo %w em fmt.Errorf?
O verbo %w “embrulha” (wraps) um error existente dentro de um novo error formatado. O erro original fica acessível via errors.Unwrap(), errors.Is() e errors.As(). Diferente de %v que apenas converte o erro para texto, %w preserva a referência ao erro original, permitindo inspeção da cadeia de erros completa.
O pacote fmt é seguro para uso concorrente?
As funções do pacote fmt que escrevem para io.Writer são seguras para uso concorrente apenas se o Writer subjacente for thread-safe. As funções Sprintf, Sprint e Sprintln são sempre seguras pois não compartilham estado. Para escrita concorrente em stdout/stderr, considere usar o pacote log que inclui sincronização interna via mutex.