Переменные в Go
Переменная — это место хранения значения, позволяющее динамически изменять хранимое значение во время выполнения. При объявлении переменной выделяется память для хранения значения соответствующего типа. Подробнее см. Справочное руководство - Переменные.
Объявление
В Go объявление типа происходит после имени переменной. Для объявления переменной используется ключевое слово var, формат: var имя_переменной тип_имя. Имя переменной должно соответствовать правилам именования идентификаторов.
var intNum int
var str string
var char byteПри объявлении нескольких переменных одного типа можно указать тип один раз:
var numA, numB, numC intПри объявлении нескольких переменных разных типов можно использовать (). Можно использовать несколько ():
var (
name string
age int
address string
)
var (
school string
class int
)Если переменная только объявлена, но не инициализирована, её значение равно нулевому значению соответствующего типа.
Присваивание
Для присваивания используется оператор =, например:
var name string
name = "jack"Также можно присвоить значение при объявлении:
var name string = "jack"Или так:
var name string
var age int
name, age = "jack", 1Второй способ требует указания типа каждый раз. Можно использовать синтаксический сахар от создателей языка: короткая инициализация переменной, которая позволяет опустить ключевое слово var и тип после имени — тип выводится компилятором.
name := "jack" // переменная строкового типаХотя тип можно не указывать, при последующем присваивании тип должен оставаться тем же. Следующий код не скомпилируется:
a := 1
a = "1"Также следует отметить, что короткая инициализация не может использовать nil, поскольку nil не принадлежит ни к какому типу, и компилятор не может вывести его тип:
name := nil // не скомпилируетсяКороткое объявление может инициализировать несколько переменных:
name, age := "jack", 1Короткое объявление нельзя использовать для уже существующей переменной:
// Неправильный пример
var a int
a := 1
// Неправильный пример
a := 1
a := 2Однако есть исключение: можно присвоить значение старой переменной одновременно с объявлением новой:
a := 1
a, b := 2, 2Такой код скомпилируется: переменная a получает новое значение, а b объявляется новой.
В Go есть правило: все переменные внутри функции должны быть использованы. Например, следующий код просто объявляет переменную, но не использует её:
func main() {
a := 1
}При компиляции возникнет ошибка, указывающая, что переменная объявлена, но не использована:
a declared and not usedЭто правило применяется только к переменным внутри функций; для переменных уровня пакета за пределами функций такого ограничения нет. Следующий код скомпилируется:
var a = 1
func main() {
}Анонимность
С помощью подчёркивания можно указать, что переменная не нужна:
Open(name string) (*File, error)Например, функция os.Open возвращает два значения, но нужно только первое. Можно написать так:
file, _ := os.Open("readme.txt")Неиспользуемая переменная не скомпилируется. Когда переменная не нужна, можно использовать подчёркивание _.
Обмен значениями
В Go для обмена значениями двух переменных не нужны указатели — можно использовать оператор присваивания напрямую. Синтаксис выглядит интуитивно понятным:
num1, num2 := 25, 36
num1, num2 = num2, num1То же самое для трёх переменных:
num1, num2, num3 := 25, 36, 49
num1, num2, num3 = num3, num2, num1Рассмотрим следующий код — это часть кода для вычисления чисел Фибоначчи. Каковы значения трёх переменных после вычисления?
a, b, c := 0, 1, 1
a, b, c = b, c, a+bОтвет:
1 1 1Вы можете спросить, почему не:
1 1 2Ведь a уже присвоено значение b, почему результат a+b всё ещё 1? При присваивании нескольких переменных в Go значения сначала вычисляются, а затем присваиваются — не слева направо.
a, b, c = b, c, a+bМожет показаться, что это разворачивается в:
a = b
b = c
c = a + bНо на самом деле значения a, b, c вычисляются заранее, затем присваиваются. Это эквивалентно:
a, b, c = 1, 1, 0+1При вызове функций этот эффект ещё заметнее. Есть функция sum, вычисляющая сумму двух чисел:
func sum(a, b int) int {
return a + b
}Используем функцию для сложения:
a, b, c := 0, 1, 1
a, b, c = b, c, sum(a, b)Результат не меняется: при вычислении возвращаемого значения sum аргументы всё ещё 0 и 1:
1 1 1Поэтому код следует записать так:
a, b = b, c
c = a + bСравнение
Сравнение переменных возможно только при условии, что их типы одинаковы. В Go нет неявного преобразования типов. Следующий код не скомпилируется:
func main() {
var a uint64
var b int64
fmt.Println(a == b)
}Компилятор сообщит, что типы не совпадают:
invalid operation: a == b (mismatched types uint64 and int64)Поэтому необходимо явное преобразование типов:
func main() {
var a uint64
var b int64
fmt.Println(int64(a) == b)
}До появления дженериков встроенные функции min и max в Go поддерживали только числа с плавающей точкой. В версии 1.21 эти функции были переписаны с использованием дженериков. Теперь можно использовать min для поиска минимума:
minVal := min(1, 2, -1, 1.2)И max для поиска максимума:
maxVal := max(100, 22, -1, 1.12)Их параметры поддерживают все сравнимые типы. Сравнимые типы в Go:
- Логические
- Числа
- Строки
- Указатели
- Каналы (только проверка на равенство)
- Массивы, элементы которых являются сравнимыми типами (срезы несравнимы) (только проверка на равенство) (только сравнение массивов одинаковой длины, поскольку длина массива — часть типа, а разные типы несравнимы)
- Структуры, все поля которых являются сравнимыми типами (только проверка на равенство)
Кроме того, можно импортировать стандартную библиотеку cmp для сравнения, но она поддерживает только упорядоченные типы. В Go встроенные упорядоченные типы — только числа и строки:
import "cmp"
func main() {
cmp.Compare(1, 2)
cmp.Less(1, 2)
}Блоки кода
Внутри функции можно создать блок кода с помощью фигурных скобок. Области видимости переменных в разных блоках независимы. Например:
func main() {
a := 1
{
a := 2
fmt.Println(a)
}
{
a := 3
fmt.Println(a)
}
fmt.Println(a)
}Вывод:
2
3
1Переменные в разных блоках независимы, не влияют друг на друга и недоступны из других блоков, но могут быть затронуты родительским блоком:
func main() {
a := 1
{
a := 2
fmt.Println(a)
}
{
fmt.Println(a)
}
fmt.Println(a)
}Вывод:
2
1
1