Desvendando o package fmt do Go
O pacote fmt é um pacote padrão do Go e muito utilizado, porém para quem esta iniciando em Go pode parecer um pouco confuso.
Afinal o que é o pacote fmt?
O pacote fmt (format) é utilizado para formatar os dados de entrada e saída, é muito utilizado para debugs, formatação, interação entre usuário e stdout entre outros. O pacote fmt é muito poderoso e existem muitas possibilidades, vamos abordar as formas mais comuns de utilizar esse pacote.
Como o Go utiliza tipagem forte, logar o valor no stdout não é como em JavaScript por exemplo, que utilizamos console.log("Hello World!")
e colocamos praticamente qualquer tipo de dado, em Go para cada tipo de dado, tem seu formatador adequado.
Se você deseja apenas exibir o valor no terminal para fim de debug, você pode utilizar o Print, existe algumas variações:
Print
Println
Printf
fmt.Print
é o mais simples, apenas imprime no stdout o valor que for informado:
package main
import "fmt"
func main() {
t := "Hello World!"
fmt.Print(t)
fmt.Print(t)
}
Saida:
Hello World!Hello World!%
Como você pode perceber, fica um pouco estranho a formatação, mas podemos separar em outra linha, para isso usamos o fmt.Prinltn
.
fmt.Println
: Tem a mesma funcionalidade do fmt.Print
, mas com quebra de linha, também podemos passar alguns parâmetros.
package main
import "fmt"
func main() {
t := "Hello World!"
fmt.Println(t)
fmt.Println(t)
}
Saida:
Hello World!
Hello World!
Como podemos ver, já ficou melhor a formatação, podemos passar mais valores:
package main
import "fmt"
func main() {
t := "Hello World!"
p := "em GO"
fmt.Println(t, p)
fmt.Println(t)
}
Saida:
Hello World! em GO
Hello World!
fmt.Printf
: Usamos o fmt.Printf
quando queremos passar variáveis.
package main
import "fmt"
func main() {
p := "em GO"
fmt.Printf("Hello Word! %s", p)
}
Saida:
Hello Word! em GO
Temos o basicamente o mesmo resultado do exemplo usando fmt.Prinln
, mas agora podemos imprimir o valor da variável com mais controle, porém se utilizarmos da seguinte forma:
package main
import "fmt"
func main() {
p := "em GO"
fmt.Printf("Hello Word! %s", p)
fmt.Printf("Hello Word! %s", p)
}
Saida:
Hello Word! em GOHello Word! em GO
Temos o problema da formatação da linha, para isso podemos usar o \n
isso faz com que a linha seja quebrada.
func main() {
p := "em GO"
fmt.Printf("Hello Word! %s \n", p)
fmt.Printf("Hello Word! %s", p)
}
Formatadores
Mas afinal, o que significa o %s
?
São formatadores muito utilizados no pacote fmt, eles determinam o tipo do dado que você deseja logar
%s
- Vai retornar a string da variável
func main() {
p := "em GO"
fmt.Printf("Hello Word! %s \n", p)
}
Saida:
Hello Word! em GO
Mas e se a minha variável p
for um int? Nesse caso vai ficar assim:
func main() {
p := 10
fmt.Printf("Hello Word! %s \n", p)
}
Saida:
Hello Word! %!s(int=10)
Desta forma não funciona, pois o p
é um int
, mas o %s
só aceita strings, %!s
significa que houve um erro ao tentar formatar.
%v
- Este é um formatter de valor genérico. Ele pode ser usado para imprimir o valor de uma variedade de tipos de dados, incluindo strings, números, estruturas, slices e muito mais.
Usando o exemplo acima, se usarmos o %v
vai funcionar perfeitamente:
func main() {
p := 10
fmt.Printf("Hello Word! %s \n", p)
}
Saida:
Hello Word! 10
o fmt apenas troca o %v
pelo valor do p
.
%d
- Para o nosso exemplo este seria o formatter mais adequado, ele é usado para informar que o formatter vai receber um tipo int
.
func main() {
p := 10
fmt.Printf("Hello Word! %d \n", p)
}
Saida:
Hello Word! 10
%f
- Este formatter é para tipo float
func main() {
p := 10.5
fmt.Printf("Hello Word! %f \n", p)
}
Saida:
Hello Word! 10.500000
Existem mais algumas coisas legais que podem ser feita com formatadores, digamos que deseja limitar o número de casas decimais do float, você pode fazer o seguinte:
func main() {
p := 10.5
fmt.Printf("Hello Word! %.2f \n", p)
}
Saida:
Hello Word! 10.50
usando %.2f
indica que queremos apenas 2 casas decimais.
Podemos também logar structs, veja um exemplo:
type Person struct {
Name string
}
func main() {
p := Person{"John"}
fmt.Printf("Hi! %s", p)
}
Saida:
Hi! {John}
Para structs geralmente usamos o %v
, se quiser ter mais detalhes da struct, para fins de debug, você pode usar o formater %#v
type Person struct {
Name string
Age int
}
func main() {
p := Person{"John", 28}
fmt.Printf("Hi! %#v", p)
}
Saida:
Hi! main.Person{Name:"John", Age:28}
Existem muitas opções de formatadores, você pode ver nas docs as opções possíveis.
Digamos que você não quer colocar no stdout (logar) o valor, quer apenas concatenar as variáveis, é possível? Sim! você pode usar o fmt.Sprint
func main() {
n := "John Doe"
p := fmt.Sprintf("Hi! %s", n)
fmt.Println(p)
}
Saida:
Hi! John Doe
Estamos concatenando o valor de n
com a string Hi!
, depois “logamos” usando o fmt.Println
.
O fmt.Sprint
tem a mesma funcionalidade do fmt.Print
, porém não é enviado para o stdout.
Uma coisa bacana é que o fmt.Sprint
transforma tudo em string, seria equivalente ao toString
do JavaScript.
Mas o fmt possui outras funcionalidades, como fmt.ErrorF
que além de logar um erro, retorna o erro.
package main
import (
"encoding/json"
"errors"
"fmt"
)
type Person struct {
Name string
}
func ReturnPerson() (*Person, error) {
var person Person
err := json.Unmarshal([]byte(`{"name": "John"}`), &person)
if err != nil {
return nil, err
}
return &person, nil
}
func main() {
_, err := ReturnPerson()
if err != nil {
fmt.Println("error to return person", err)
return
}
}
No exemplo acima, a função ReturnPerson()
retorna um ponteiro de Person
ou error
, mas não temos detalhes de log do erro, precisaríamos colocar antes do return, para detalhar melhor o log:
func main() {
_, err := ReturnPerson()
if err != nil {
fmt.Println("error to return person", err)
return
}
}
Se utilizarmos o fmt.Errorf
, podemos simplificar o código:
func ReturnPerson() (*Person, error) {
var person Person
err := json.Unmarshal([]byte(`"name": "John"}`), &person) // json errado para capturar um erro
if err != nil {
return nil, fmt.Errorf("error to unmarshal person: %w", err)
}
return &person, nil
}
func main() {
_, err := ReturnPerson()
if err != nil {
return
}
}
Colocando o fmt.Errorf
no retorno do erro da função ReturnPerson()
, vamos ter o erro com uma mensagem mais clara e também o erro original, ao utilizar o ReturnPerson()
não precisamos colocar mais detalhes, apenas tratar o erro ou apenas fazer um return
.
É claro que o pacote fmt não é recomendado para criar logs em produção, ele afeta consideravelmente a performance do seu código, para isso é recomendado utilizar o pacote padrão para log do Go, pacotes de terceiros como o zap log da Uber, que é muito utilizado para fazer logs ou até o recém chegado no Go o slog.
Considerações finais
Nesses exemplos simples você pode perceber como é simples entender o pacote fmt do Go, mesmo que o uso dos formatadores podem ser confusos no inicio, mas com o tempo fica muito claro. É um dos pacotes padrão do Go mais utilizados e possui muitos recursos que facilitam sua vida no dia a dia, entender seu funcionamento básico pode te ajudar muito.
Até a próxima!