Em Go, todas as variáveis têm um valor. Declarar uma variável sem lhe atribuir um valor define-a como o valor zero do seu tipo.
Os tipos básicos, incluindo bool, tipos numéricos e string, têm cada um um valor zero fixo:
| Tipo | Valor zero |
|---|---|
| bool | false |
| int, int8, int64, etc. | 0 |
| float32, float64 | 0 |
| complex64, complex128 | 0+0i |
| string | "" |
Os tipos que referenciam dados subjacentes, incluindo ponteiros, funções, interfaces, slices, canais e mapas, têm cada um um valor zero de nil.
No entanto, os arrays e as structs nunca são nil, porque contêm os dados diretamente, e não uma referência a eles.
Cada elemento de um array e cada campo de uma struct é inicializado com o valor zero do seu próprio tipo.
Por exemplo, var a [3]int cria o array [0, 0, 0].
var sem um valor inicial dá o valor zero para qualquer tipo:
var myBool bool // false
var mySlice []int // nil
var p Person // Person{Name: "", Age: 0}
Para structs, um literal composto T{} é uma alternativa:
myPerson := Person{} // equivalent to var myPerson Person
new(T) devolve um ponteiro para o valor zero de qualquer tipo.
É mais comummente usado com structs, embora também funcione com tipos básicos:
p := new(Person) // *Person, pointing to Person{Name: "", Age: 0}
s := new(string) // *string, pointing to ""
i := new(int) // *int, pointing to 0
Em Go, um valor zero representa um estado inicial natural e útil.
Um bool tem como valor predefinido false, o que pode funcionar como um indicador:
var done bool // action not done yet
done = true // action now done
Um inteiro tem como valor predefinido 0, o que pode funcionar como um contador:
var count int
count++ // 1
count++ // 2
Para structs, cada campo começa com o valor zero do seu próprio tipo.
Uma Stack com um único campo []string já é uma pilha vazia funcional quando é declarada.
Como append aloca um novo array subjacente quando é chamado numa slice nil, não é preciso um construtor:
type Stack struct {
items []string
}
func (s *Stack) Push(v string) {
s.items = append(s.items, v)
}
func (s *Stack) IsEmpty() bool {
return len(s.items) == 0
}
var s Stack
fmt.Println(s.IsEmpty()) // true
s.Push("a") // append allocates a new backing array
fmt.Println(s.IsEmpty()) // false
A maioria dos tipos com valores zero nil precisa de inicialização antes de ser usada; caso contrário, provoca um panic.
Uma slice nil é a exceção: podes percorrê-la, acrescentar-lhe elementos e passá-la a len e cap em segurança.
Um mapa deve ser inicializado antes de guardar um valor:
var m map[string]int
m["key"] = 1 // panic
Um mapa é normalmente inicializado de duas formas diferentes:
m1 := make(map[string]int)
m2 := map[string]int{}
m1["key"] = 1 // ok
m2["key"] = 1 // ok
Verifica se um ponteiro é nil antes de o usares:
var p *int
if p == nil {
fmt.Println("no value assigned")
return
}
fmt.Println(*p) // p is not nil
Os tipos básicos têm sempre um valor concreto, por isso compará-los com nil é um erro de compilação:
var myString string
if myString == nil {
// compile error: mismatched types
}