У Go кожна змінна має значення. Оголошення змінної без присвоєння значення встановлює для неї нульове значення її типу.
Базові типи, зокрема bool, числові типи та string, мають фіксоване нульове значення:
| Тип | Нульове значення |
|---|---|
| bool | false |
| int, int8, int64, etc. | 0 |
| float32, float64 | 0 |
| complex64, complex128 | 0+0i |
| string | "" |
Типи, які посилаються на базові дані, зокрема вказівники, функції, інтерфейси, зрізи, канали та мапи, мають нульове значення nil.
Однак масиви та структури ніколи не дорівнюють nil, бо вони зберігають дані безпосередньо, а не посилання на них.
Кожен елемент масиву й кожне поле структури ініціалізується нульовим значенням свого типу.
Наприклад, var a [3]int створює масив [0, 0, 0].
var без початкового значення дає нульове значення для будь-якого типу:
var myBool bool // false
var mySlice []int // nil
var p Person // Person{Name: "", Age: 0}
Для структур альтернативою є складений літерал T{}:
myPerson := Person{} // equivalent to var myPerson Person
new(T) повертає вказівник на нульове значення будь-якого типу.
Найчастіше його використовують зі структурами, хоча він працює й для базових типів:
p := new(Person) // *Person, pointing to Person{Name: "", Age: 0}
s := new(string) // *string, pointing to ""
i := new(int) // *int, pointing to 0
У Go нульове значення є природним і корисним початковим станом.
bool типово має значення false, що може слугувати прапорцем:
var done bool // action not done yet
done = true // action now done
Ціле число типово має значення 0, що може слугувати лічильником:
var count int
count++ // 1
count++ // 2
У структурах кожне поле починається з нульового значення свого типу.
Stack з єдиним полем []string уже є робочим порожнім стеком після оголошення.
Оскільки append під час виклику на зрізі зі значенням nil виділяє новий базовий масив, конструктор не потрібен:
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
Більшість типів із нульовим значенням nil потребують ініціалізації перед використанням, інакше вони спричиняють паніку.
Винятком є зріз nil: його можна перебирати, додавати до нього елементи та безпечно передавати до len і cap.
Мапу слід ініціалізувати перед збереженням значення:
var m map[string]int
m["key"] = 1 // panic
Мапу зазвичай ініціалізують двома різними способами:
m1 := make(map[string]int)
m2 := map[string]int{}
m1["key"] = 1 // ok
m2["key"] = 1 // ok
Перевірмо вказівник на nil перед використанням:
var p *int
if p == nil {
fmt.Println("no value assigned")
return
}
fmt.Println(*p) // p is not nil
Базові типи завжди містять конкретне значення, тому порівняння їх із nil є помилкою компіляції:
var myString string
if myString == nil {
// compile error: mismatched types
}