:= и = в Go: в чём разница? Полный разбор с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему путаница между := и = может сломать ваш Go-код

Вы пишете на Go, всё идёт отлично, но вдруг компилятор выдаёт ошибку no new variables on left side of :=. Знакомо? Или вы видите код коллеги, где он использует то =, то :=, и кажется, что разницы нет. На самом деле разница принципиальная, и непонимание этого — одна из самых частых ошибок у новичков в Golang.

В этом руководстве мы разберём, чем отличаются операторы := (short variable declaration) и = (assignment). Вы узнаете, когда какой использовать, как не нарваться на ошибки и как писать более чистый и идиоматичный код на Go. Поехали!

Основы: что такое переменные и зачем они нужны?

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

Операторы := и = — это два способа взаимодействия с переменными, но они решают разные задачи. Первый — объявляет переменную, второй — изменяет её значение.

Оператор = (присваивание): только изменение, без объявления

Оператор = — это классическое присваивание. Он берёт уже существующую переменную и записывает в неё новое значение. Если переменной не существует, компилятор выдаст ошибку.

package main

import "fmt"

func main() {
    var name string  // объявили переменную явно
    name = "Go"      // присвоили значение (работает)
    fmt.Println(name)

    // age = 10       // ОШИБКА: переменная age не объявлена
}

Здесь name была объявлена с помощью var, поэтому мы можем использовать =. Если бы мы попробовали написать age = 10 без предварительного объявления, компилятор бы отказался компилировать код.

Когда использовать =?

  • Когда переменная уже объявлена (через var или :=).
  • В циклах и итерациях для обновления счётчиков.
  • При работе с указателями и структурами, где нужно изменить поле.

Оператор := (короткое объявление): объявление + присваивание за один шаг

Оператор := — это "синтаксический сахар", который одновременно объявляет переменную и присваивает ей значение. Тип переменной выводится автоматически из выражения справа. Это гораздо короче, чем писать var x int = 5.

package main

import "fmt"

func main() {
    name := "Go"   // объявили и присвоили, тип string
    age := 10      // объявили и присвоили, тип int
    fmt.Println(name, age)
}

Это эквивалентно:

var name string = "Go"
var age int = 10

Но := короче и читается легче. Главное правило: слева от := должна быть хотя бы одна новая переменная. Если все переменные уже объявлены, вы получите ошибку.

Когда использовать :=?

  • Для объявления локальных переменных внутри функций.
  • В циклах for, if и switch с короткой инициализацией.
  • При работе с функциями, возвращающими несколько значений (например, err := someFunc()).

Главное отличие: объявление vs изменение

Самое важное различие, которое нужно запомнить:

  • := — это объявление (declaration). Оно создаёт новую переменную в текущей области видимости.
  • = — это присваивание (assignment). Оно изменяет существующую переменную.

Попробуйте представить это как разницу между "рождением" переменной и "изменением её имени". Вы не можете изменить имя человека, которого ещё нет на свете.

Сложные случаи: несколько переменных и переобъявление

Go допускает интересный трюк: если слева от := есть хотя бы одна новая переменная, остальные могут быть уже существующими. Это называется переобъявление (redeclaration).

package main

import "fmt"

func main() {
    x := 10
    y := 20

    // x уже существует, но z — новая. Это разрешено!
    x, z := 30, 40
    fmt.Println(x, y, z) // Вывод: 30 20 40
}

Здесь x получит новое значение, а z будет объявлена. Это очень удобно при работе с ошибками:

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println(err)
        return
    }
    // ... работаем с file

    // Пытаемся открыть другой файл
    otherFile, err := os.Open("other.txt") // err уже существует, но otherFile — новый
    if err != nil {
        fmt.Println(err)
        return
    }
    _ = otherFile
}

Обратите внимание: err не создаётся заново, а просто перезаписывается. Это экономит память и делает код чище.

Типичные ошибки и как их избежать

Ошибка 1: Использование := с уже объявленной переменной

package main

func main() {
    x := 5
    x := 10 // ОШИБКА: no new variables on left side of :=
}

Решение: Используйте = для изменения x.

Ошибка 2: Использование = для несуществующей переменной

package main

func main() {
    x = 5 // ОШИБКА: undefined: x
}

Решение: Объявите переменную через var или :=.

Ошибка 3: Путаница с областью видимости

package main

import "fmt"

func main() {
    x := 10
    if true {
        x := 20 // Это НОВАЯ переменная x в области видимости if
        fmt.Println(x) // 20
    }
    fmt.Println(x) // 10 (внешняя переменная не изменилась)
}

Здесь := внутри if создаёт новую переменную, которая "затеняет" внешнюю. Если вы хотите изменить внешнюю, используйте =.

Практические советы: как писать идиоматичный Go