Некоторые операторы: инкремента/декремента и присваивания
Краткое описание
В этом уроке разбираем операторы инкремента (++) и декремента (--) в Go, и почему они радикально отличаются от C#, Java, JavaScript. Изучаем составные операторы присваивания (+=, -=, *=, /=, %=) и понимаем, почему x = x + 1 — это не математическое равенство. Кратко касаемся побитовых операций.
Ключевые концепции
Инкремент и декремент: два критических отличия
В Go операторы ++ и -- работают совершенно иначе, чем в других языках.
Отличие 1: только постфиксная форма
В Go разрешена ТОЛЬКО постфиксная форма:
counter := 5
counter++ // ✅ OK: постфиксный инкремент
counter-- // ✅ OK: постфиксный декремент
// ++counter // ❌ ОШИБКА компиляции!
// --counter // ❌ ОШИБКА компиляции!
В других языках (C#, Java, JS):
let x = 5;
x++; // работает
++x; // тоже работает
В Go префиксная форма запрещена.
Отличие 2: statement, а не expression
Критично: ++ и -- — это операторы (statements), а не выражения (expressions). Они не возвращают значение.
counter := 5
// z := counter++ // ❌ ОШИБКА! syntax error: unexpected ++
// fmt.Println(counter++) // ❌ ОШИБКА! нельзя использовать в выражениях
В других языках (C#, Java, JS):
let counter = 5;
let z = counter++; // работает, z = 5, counter = 6
В Go такое невозможно.
Почему Go так сделали?
Разработчики Go убрали префиксные операторы и запретили использование в выражениях, чтобы:
- Устранить неоднозначность
// JavaScript - запутанный код let x = 5; let y = x++ + ++x; // что здесь происходит? - Избежать вопросов на собеседованиях в стиле “gotcha”
// Java - бессмысленный код, который спрашивают на интервью int x = 5; int y = x++ + x++ + ++x; // чему равно y? - Сделать код проще и понятнее
В Go если нужно использовать значение и увеличить его, пишите явно:
counter := 5
// Если нужно использовать старое значение
oldValue := counter
counter++
fmt.Println(oldValue) // 5
// Или просто
counter = counter + 1
Правильное использование инкремента/декремента
counter := 5
fmt.Println(counter) // 5
counter++ // увеличение на 1
fmt.Println(counter) // 6
counter-- // уменьшение на 1
fmt.Println(counter) // 5
// Инкремент - это отдельная инструкция
counter++
// Дальше используем изменённое значение
fmt.Println(counter) // 6
Запомните: counter++ — это полноценный оператор, занимающий отдельную строку.
Составные операторы присваивания
Если нужно изменить переменную и использовать результат, используйте составные операторы:
value := 10
value += 5 // value = value + 5 → 15
value -= 3 // value = value - 3 → 12
value *= 2 // value = value * 2 → 24
value /= 4 // value = value / 4 → 6
value %= 4 // value = value % 4 → 2
Эквивалентность:
x += y⟺x = x + yx -= y⟺x = x - yx *= y⟺x = x * yx /= y⟺x = x / yx %= y⟺x = x % y
Присваивание ≠ математическое равенство
Важно понимать: оператор = — это не равенство, а присваивание.
value := 10
// НЕ читать как "value равно value плюс один"
// ЧИТАТЬ как "вычисли value + 1 и положи результат в value"
value = value + 1
Порядок выполнения:
- Вычислить правую часть (
value + 1→10 + 1→11) - Присвоить результат переменной слева (
value = 11)
Слева от = может быть только переменная:
x := 5
x = x + 1 // ✅ OK
// x + 1 = x // ❌ ОШИБКА! слева не переменная
Побитовые операции (краткий обзор)
Go поддерживает стандартные побитовые операции для работы с двоичным представлением чисел:
a := 5 // 0101 в двоичной системе
b := 3 // 0011
// Побитовое И (AND)
result := a & b // 0101 & 0011 = 0001 → 1
// Побитовое ИЛИ (OR)
result = a | b // 0101 | 0011 = 0111 → 7
// Побитовое XOR (исключающее ИЛИ)
result = a ^ b // 0101 ^ 0011 = 0110 → 6
// Сдвиг влево (умножение на 2)
result = 5 << 1 // 0101 << 1 = 1010 → 10
// Сдвиг вправо (деление на 2)
result = 5 >> 1 // 0101 >> 1 = 0010 → 2
Составные побитовые операторы:
x &= y // x = x & y
x |= y // x = x | y
x ^= y // x = x ^ y
x <<= 2 // x = x << 2
x >>= 2 // x = x >> 2
Практические примеры
Инкремент/декремент
package main
import "fmt"
func main() {
counter := 5
fmt.Printf("Начальное значение: %d\n", counter) // 5
counter++
fmt.Printf("После counter++: %d\n", counter) // 6
counter--
fmt.Printf("После counter--: %d\n", counter) // 5
// ОШИБКИ - так делать нельзя:
// ++counter // ❌ синтаксическая ошибка
// z := counter++ // ❌ нельзя использовать в выражении
// fmt.Println(counter++) // ❌ нельзя передать в функцию
// ПРАВИЛЬНО:
counter = counter + 1
fmt.Printf("После counter = counter + 1: %d\n", counter) // 6
counter += 1
fmt.Printf("После counter += 1: %d\n", counter) // 7
}
Составные операторы
value := 10
fmt.Printf("Начало: %d\n", value) // 10
value += 5
fmt.Printf("После += 5: %d\n", value) // 15
value -= 3
fmt.Printf("После -= 3: %d\n", value) // 12
value *= 2
fmt.Printf("После *= 2: %d\n", value) // 24
value /= 4
fmt.Printf("После /= 4: %d\n", value) // 6
value %= 4
fmt.Printf("После %= 4: %d\n", value) // 2
Отличие от других языков
JavaScript/Java/C# (так НЕ работает в Go):
// JavaScript - компактно, но запутанно
let x = 5;
let y = x++ + ++x; // x=7, y=12 🤯
// Использование в функциях
console.log(x++); // выведет 5, x станет 6
Go (явно и понятно):
// Go - многословно, но прозрачно
x := 5
oldX := x
x++
x++
y := oldX + x // 5 + 7 = 12
// Использование в функциях
fmt.Println(x) // выведет текущее значение
x++ // увеличение - отдельная операция
Важные моменты
1. Только постфиксная форма
x++ // ✅ работает
++x // ❌ ошибка компиляции
2. Не является выражением
y := x++ // ❌ ошибка
fmt.Println(x++) // ❌ ошибка
if x++ == 10 {} // ❌ ошибка
3. Отдельная инструкция
// Правильно
x++
fmt.Println(x)
// Неправильно
fmt.Println(x++) // ❌
4. Составные операторы — альтернатива
// Вместо x++ в выражении:
x += 1
y := x // теперь можно использовать
5. Присваивание выполняется справа налево
x = x + 10 // сначала вычисляется x + 10, потом присваивается в x
6. Побитовые операции с составными операторами
x &= 0xFF // x = x & 0xFF (маскирование)
x <<= 1 // x = x << 1 (умножение на 2)
Best Practices
1. Используйте ++ и -- отдельными строками
// Хорошо
counter++
processValue(counter)
// Плохо (не компилируется, но даже если бы...)
// processValue(counter++)
2. Для сложных операций используйте явное присваивание
// Хорошо - понятно
oldValue := counter
counter++
calculate(oldValue, counter)
// Плохо - так в Go нельзя
// calculate(counter++, counter)
3. Составные операторы для читаемости
// Хорошо
total += price
// Хуже
total = total + price
4. Комментируйте побитовые операции
// Устанавливаем флаг в 3-м бите
flags |= (1 << 3)
// Проверяем флаг
if flags & (1 << 3) != 0 {
// флаг установлен
}
Что запомнить
- В Go
++и--— только постфиксные (x++, а не++x) - Это операторы (statements), а не выражения (expressions)
- Нельзя использовать в выражениях:
z := x++— ошибка - Нельзя передавать в функции:
fmt.Println(x++)— ошибка - Для использования в выражениях:
x += 1вместоx++ - Составные операторы:
+=,-=,*=,/=,%= x = x + 1— это присваивание, а не математическое равенство- Выполнение: сначала правая часть, потом присваивание в левую
- Побитовые операции:
&,|,^,<<,>> - Составные побитовые:
&=,|=,^=,<<=,>>= - Go проще других языков — нет запутанных конструкций с
++