Skip to content

Переменные в Go

Переменная — это место хранения значения, позволяющее динамически изменять хранимое значение во время выполнения. При объявлении переменной выделяется память для хранения значения соответствующего типа. Подробнее см. Справочное руководство - Переменные.

Объявление

В Go объявление типа происходит после имени переменной. Для объявления переменной используется ключевое слово var, формат: var имя_переменной тип_имя. Имя переменной должно соответствовать правилам именования идентификаторов.

go
var intNum int
var str string
var char byte

При объявлении нескольких переменных одного типа можно указать тип один раз:

go
var numA, numB, numC int

При объявлении нескольких переменных разных типов можно использовать (). Можно использовать несколько ():

go
var (
  name    string
  age     int
  address string
)

var (
  school string
  class int
)

Если переменная только объявлена, но не инициализирована, её значение равно нулевому значению соответствующего типа.

Присваивание

Для присваивания используется оператор =, например:

go
var name string
name = "jack"

Также можно присвоить значение при объявлении:

go
var name string = "jack"

Или так:

go
var name string
var age int
name, age = "jack", 1

Второй способ требует указания типа каждый раз. Можно использовать синтаксический сахар от создателей языка: короткая инициализация переменной, которая позволяет опустить ключевое слово var и тип после имени — тип выводится компилятором.

go
name := "jack" // переменная строкового типа

Хотя тип можно не указывать, при последующем присваивании тип должен оставаться тем же. Следующий код не скомпилируется:

a := 1
a = "1"

Также следует отметить, что короткая инициализация не может использовать nil, поскольку nil не принадлежит ни к какому типу, и компилятор не может вывести его тип:

go
name := nil // не скомпилируется

Короткое объявление может инициализировать несколько переменных:

go
name, age := "jack", 1

Короткое объявление нельзя использовать для уже существующей переменной:

go
// Неправильный пример
var a int
a := 1

// Неправильный пример
a := 1
a := 2

Однако есть исключение: можно присвоить значение старой переменной одновременно с объявлением новой:

go
a := 1
a, b := 2, 2

Такой код скомпилируется: переменная a получает новое значение, а b объявляется новой.

В Go есть правило: все переменные внутри функции должны быть использованы. Например, следующий код просто объявляет переменную, но не использует её:

go
func main() {
  a := 1
}

При компиляции возникнет ошибка, указывающая, что переменная объявлена, но не использована:

a declared and not used

Это правило применяется только к переменным внутри функций; для переменных уровня пакета за пределами функций такого ограничения нет. Следующий код скомпилируется:

go
var a = 1

func main() {

}

Анонимность

С помощью подчёркивания можно указать, что переменная не нужна:

go
Open(name string) (*File, error)

Например, функция os.Open возвращает два значения, но нужно только первое. Можно написать так:

go
file, _ := os.Open("readme.txt")

Неиспользуемая переменная не скомпилируется. Когда переменная не нужна, можно использовать подчёркивание _.

Обмен значениями

В Go для обмена значениями двух переменных не нужны указатели — можно использовать оператор присваивания напрямую. Синтаксис выглядит интуитивно понятным:

go
num1, num2 := 25, 36
num1, num2 = num2, num1

То же самое для трёх переменных:

go
num1, num2, num3 := 25, 36, 49
num1, num2, num3 = num3, num2, num1

Рассмотрим следующий код — это часть кода для вычисления чисел Фибоначчи. Каковы значения трёх переменных после вычисления?

go
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 значения сначала вычисляются, а затем присваиваются — не слева направо.

go
a, b, c = b, c, a+b

Может показаться, что это разворачивается в:

go
a = b
b = c
c = a + b

Но на самом деле значения a, b, c вычисляются заранее, затем присваиваются. Это эквивалентно:

go
a, b, c = 1, 1, 0+1

При вызове функций этот эффект ещё заметнее. Есть функция sum, вычисляющая сумму двух чисел:

go
func sum(a, b int) int {
  return a + b
}

Используем функцию для сложения:

go
a, b, c := 0, 1, 1
a, b, c = b, c, sum(a, b)

Результат не меняется: при вычислении возвращаемого значения sum аргументы всё ещё 0 и 1:

1 1 1

Поэтому код следует записать так:

go
a, b = b, c
c = a + b

Сравнение

Сравнение переменных возможно только при условии, что их типы одинаковы. В Go нет неявного преобразования типов. Следующий код не скомпилируется:

go
func main() {
  var a uint64
  var b int64
  fmt.Println(a == b)
}

Компилятор сообщит, что типы не совпадают:

invalid operation: a == b (mismatched types uint64 and int64)

Поэтому необходимо явное преобразование типов:

go
func main() {
  var a uint64
  var b int64
  fmt.Println(int64(a) == b)
}

До появления дженериков встроенные функции min и max в Go поддерживали только числа с плавающей точкой. В версии 1.21 эти функции были переписаны с использованием дженериков. Теперь можно использовать min для поиска минимума:

go
minVal := min(1, 2, -1, 1.2)

И max для поиска максимума:

go
maxVal := max(100, 22, -1, 1.12)

Их параметры поддерживают все сравнимые типы. Сравнимые типы в Go:

  • Логические
  • Числа
  • Строки
  • Указатели
  • Каналы (только проверка на равенство)
  • Массивы, элементы которых являются сравнимыми типами (срезы несравнимы) (только проверка на равенство) (только сравнение массивов одинаковой длины, поскольку длина массива — часть типа, а разные типы несравнимы)
  • Структуры, все поля которых являются сравнимыми типами (только проверка на равенство)

Кроме того, можно импортировать стандартную библиотеку cmp для сравнения, но она поддерживает только упорядоченные типы. В Go встроенные упорядоченные типы — только числа и строки:

go
import "cmp"

func main() {
  cmp.Compare(1, 2)
  cmp.Less(1, 2)
}

Блоки кода

Внутри функции можно создать блок кода с помощью фигурных скобок. Области видимости переменных в разных блоках независимы. Например:

go
func main() {
  a := 1

  {
    a := 2
    fmt.Println(a)
  }

  {
    a := 3
    fmt.Println(a)
  }
  fmt.Println(a)
}

Вывод:

2
3
1

Переменные в разных блоках независимы, не влияют друг на друга и недоступны из других блоков, но могут быть затронуты родительским блоком:

go
func main() {
  a := 1

  {
    a := 2
    fmt.Println(a)
  }

  {
    fmt.Println(a)
  }
  fmt.Println(a)
}

Вывод:

2
1
1

Golang by www.golangdev.cn edit