Змінні та константи¶
var — повна форма¶
var оголошує одну або кілька змінних. Підтримуються чотири форми.
// 1. Тип з ініціалізатором.
var name string = "Ada"
var u, v, w float64 = -1.0, -2.0, -3.0
// 2. Тип без ініціалізатора — змінна отримує нульове значення.
var counter int // counter == 0
var greeting string // greeting == ""
var active bool // active == false
// 3. Ініціалізатор без явного типу — тип виводиться автоматично.
var k = 0 // k is int
var pi = 3.14 // pi is float64
// 4. Згруповані оголошення (поширено на рівні пакету).
var (
host = "localhost"
port = 8080
verbose bool
)
Нульові значення¶
Зі специфікації Go:
"В іншому випадку кожна змінна ініціалізується своїм нульовим значенням."
У Go немає «неініціалізованого» стану — кожна змінна завжди має значення.
| Тип | Нульове значення |
|---|---|
числові (int, float64, …) |
0 |
bool |
false |
string |
"" |
| вказівник, зріз, мапа, канал, функція, інтерфейс | nil |
| структура | структура, поля якої мають власні нульові значення |
var s []int // s == nil, len(s) == 0
var m map[string]int // m == nil — читання коректне, запис спричинить паніку
var p *int // p == nil
:= — коротке оголошення¶
Усередині функції := оголошує та присвоює за один крок. Тип завжди виводиться автоматично.
func main() {
name := "Ada" // string
count := 0 // int
ratio := 0.5 // float64
items := []string{"a", "b"} // []string — зріз; розглянемо пізніше
fmt.Println(name, count, ratio, items)
}
Два правила, які варто запам'ятати:
- Тільки в межах функції.
:=заборонено на рівні пакету — там потрібно використовуватиvar. - Повторне оголошення дозволяється, якщо хоча б одне ім'я ліворуч є новим, а ті, що повторюються, зберігають той самий тип:
З досвіду Python: Python дозволяє присвоювати де завгодно без зайвих оголошень. У Go є два способи (
varта:=): перший — єдиний дозволений на рівні пакету, другий дає зручний висновок типу та підтримку множинного повернення всередині функцій.
Множинне присвоєння та порожній ідентифікатор _¶
Схоже на розпакування кортежу у Python, але для повернення значень із функцій:
n, err := strconv.Atoi("42")
if err != nil {
return err
}
fmt.Println(n)
_, err = io.Copy(dst, src) // ігноруємо кількість байтів, зберігаємо помилку
_ — це порожній ідентифікатор: слот «лише для запису», що дозволяє відкинути непотрібне значення.
Пастка затінення¶
Новий := у внутрішній області видимості створює нову змінну, яка затінює зовнішню. Читайте уважно:
err := doFirst()
if err != nil { return err }
if cond {
err := doSecond() // !!! НОВА err, затінює зовнішню
log.Println(err)
}
// зовнішня err тут досі та, що з doFirst, а НЕ з doSecond.
Це одна з найпоширеніших помилок у Go. Аналізатор тіней go vet і golangci-lint попередять вас.
const — константи часу компіляції¶
Константи обчислюються під час компіляції. Вони можуть бути типізованими або нетипізованими.
const Pi float64 = 3.14159 // типізована
const Greeting = "hello" // нетипізований рядок
const (
KB = 1024
MB = 1024 * KB
GB = 1024 * MB
)
Константі не можна присвоїти нове значення. Також не можна отримати її адресу (&Pi є помилкою компіляції).
Нетипізовані константи є гнучкими — вони набувають того типу, який потрібен у контексті:
const limit = 10
var i int = limit // limit використовується як int
var f float64 = limit // limit використовується як float64
var s string = "small"
const small = "small"
fmt.Println(s == small) // працює — small підходить для string
Якби limit було оголошено як const limit int = 10, другий рядок не скомпілювався б — int не можна присвоїти float64 без явного перетворення.
iota — константи з автоматичним інкрементом¶
iota — це зумовлений ідентифікатор, який скидається до 0 на початку кожного блоку const і збільшується на 1 з кожним ConstSpec.
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
Вираз у кожному рядку неявно повторюється з попереднього рядка. Можна використовувати зі зсувами бітів для прапорців-перерахувань:
const (
ReadPerm = 1 << iota // 1 (iota == 0)
WritePerm // 2 (iota == 1)
ExecutePerm // 4 (iota == 2)
)
perms := ReadPerm | WritePerm // == 3
iota існує лише всередині блоків const — загального синтаксису лічильника не існує.