Урок 2. Переменные, := и типы
Программа из первого урока всегда печатает одно и то же. Чтобы она что-то считала, значения нужно где-то хранить. Для этого есть переменные — именованные места в памяти. У каждой переменной в Go есть тип: он задаётся один раз и потом не меняется. В переменную типа int (целое число) нельзя положить строку — компилятор не даст.
Объявление: var и :=
Полная запись — служебное слово var, имя, тип и значение:
package main
import "fmt"
func main() {
var age int = 20
var name = "Анна"
city := "Минск"
fmt.Println(name, age, city)
age = 21
city = "Брест"
fmt.Println(name, age, city)
}Анна 20 Минск
Анна 21 БрестТри способа объявить переменную:
var age int = 20— тип указан явно;var name = "Анна"— тип компилятор выводит сам из значения (здесьstring);city := "Минск"— короткое объявление: то же, чтоvar city = "Минск". Так пишут чаще всего.
:= объявляет новую переменную, а = присваивает значение уже существующей. Короткое объявление работает только внутри функций; на уровне пакета, вне функций, пишут var.
Слева от := должна быть хотя бы одна новая переменная. Если все уже объявлены, компилятор сообщит no new variables on left side of := — значит, нужно = вместо :=.
Несколько переменных объявляются и присваиваются сразу:
package main
import "fmt"
func main() {
a, b := 1, 2
a, b = b, a
fmt.Println(a, b)
}2 1Вторая строка меняет значения местами без третьей переменной: сначала вычисляется вся правая часть, потом значения раскладываются по переменным.
Объявленная внутри функции переменная должна где-то читаться. Если переменная только объявлена или ей только присваивают значения, программа не соберётся: declared and not used: x. Подробно — в справке declared and not used.
Нулевые значения
Переменная без начального значения в Go не содержит «мусора»: она получает нулевое значение своего типа.
package main
import "fmt"
func main() {
var count int
var price float64
var name string
var ready bool
fmt.Println(count, price, ready)
fmt.Printf("%q\n", name)
}0 0 false
""У чисел нулевое значение — 0, у строки — пустая строка "", у bool — false. Пустую строку Println напечатал бы как ничто, поэтому здесь она выведена через Printf с %q — в кавычках.
Основные типы
| Тип | Что хранит | Пример |
|---|---|---|
int | целое число; на 64-битных системах — примерно ±9,2·10¹⁸ | n := 42 |
int64, int32, int8 | целое ровно на 64, 32, 8 бит | var big int64 = 1 << 40 |
uint, uint8… | целое без знака, от нуля | var u uint = 7 |
float64 | дробное число, около 15–16 верных знаков | pi := 3.14159 |
string | строка текста в двойных кавычках | s := "Go" |
bool | true или false | ok := true |
byte | то же, что uint8: один байт | b := byte('A') |
rune | один символ Юникода, то же, что int32 | r := 'ж' |
Для начала хватит int, float64, string и bool. Число без точки (42) по умолчанию становится int, с точкой (2.5) — float64. Символ в одинарных кавычках ('ж') — это rune, а строка — только в двойных.
Строки в Go хранятся в кодировке UTF-8, и len считает байты, а не буквы. Русская буква занимает два байта:
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
s := "привет"
fmt.Println(len(s))
fmt.Println(utf8.RuneCountInString(s))
fmt.Println(len([]rune(s)))
}12
6
6Количество букв дают utf8.RuneCountInString или длина среза символов []rune(s) (о срезах — в уроке Массивы, срезы и append).
Арифметика и деление
Действия: +, -, *, / и % (остаток). Как и во многих языках, деление двух целых даёт целое: дробная часть отбрасывается.
package main
import "fmt"
func main() {
a, b := 7, 2
fmt.Println(a / b)
fmt.Println(a % b)
fmt.Println(-a/b, -a%b)
fmt.Println(float64(a) / float64(b))
}3
1
-3 -1
3.5Результат деления отбрасывается к нулю, поэтому -7 / 2 — это -3; знак остатка — как у делимого.
Сокращённые записи: x += 5 значит x = x + 5, так же работают -=, *=, /=, %=. x++ и x-- увеличивают и уменьшают на единицу. В Go это отдельные команды, а не выражения: y := x++ не соберётся, и формы ++x нет вовсе.
Типы не смешиваются сами
В Go нет автоматического перевода одного числового типа в другой. Сложить int и float64 нельзя:
package main
import "fmt"
func main() {
count := 3
price := 2.5
// fmt.Println(count * price) — ошибка: mismatched types int and float64
total := float64(count) * price
fmt.Println(total)
fmt.Println(int(total))
}7.5
7float64(count) — преобразование типа: значение count берётся как дробное. int(7.5) отбрасывает дробную часть и даёт 7. Число в строку и строку в число так не переводят: string(65) даёт не "65", а букву "A". Для этого есть пакет strconv — справка Как перевести строку в число.
Константы
Значение, которое не меняется, объявляют через const:
package main
import "fmt"
const daysInWeek = 7
func main() {
const hoursInDay = 24
fmt.Println(daysInWeek * hoursInDay)
}168Константу нельзя изменить, и компилятор не требует, чтобы она где-то использовалась. Для нумерованных констант есть iota — счётчик, который в блоке const ( … ) начинается с нуля и растёт на единицу с каждой строкой.
Имена
Имя состоит из букв, цифр и _ и не начинается с цифры. В Go принято писать имена из нескольких слов слитно, со второго слова с большой буквы: userName, maxValue, а не user_name. Помните, что большая первая буква делает имя экспортируемым (видимым из других пакетов): внутри функций имена пишут с маленькой.
Частые ошибки
:=вместо=для уже объявленной переменной —no new variables on left side of :=.:=вне функции —syntax error: non-declaration statement outside function body. На уровне пакета —var.- Сложение разных типов —
invalid operation: … (mismatched types int and float64). Нужен явныйfloat64(…). 5 / 2ждут2.5, а получают2— оба числа целые.- Неиспользуемая переменная —
declared and not used.
Коротко
x := значение— короткое объявление внутри функций;var x тип— везде.=присваивает существующей переменной,:=объявляет новую.- Переменная без значения получает нулевое:
0,"",false. - Основные типы:
int,float64,string,bool; символ —rune. lenстроки — в байтах; буквы считаетutf8.RuneCountInString.- Типы не смешиваются:
float64(n),int(x)— явно.