После того как ты познакомился с типами данных и переменными, пора научиться выполнять над ними операции. Go поддерживает арифметические, логические, побитовые и строковые операции.

Операции — это основа любой программы. Они позволяют обрабатывать данные, принимать решения и создавать полезную функциональность. В этой главе мы изучим не только синтаксис операций, но и их практическое применение в реальных проектах. Особое внимание уделим безопасности кода и обработке потенциальных ошибок.


🔢 Арифметические операции

Арифметические операции — это математические действия над числовыми типами данных. Go поддерживает все стандартные арифметические операции, которые работают с целыми числами (int, int32, int64) и числами с плавающей точкой (float32, float64).

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

ОперацияСимволПримерРезультат
Сложение+2 + 35
Вычитание-5 - 23
Умножение*4 * 28
Деление/10 / 25
Остаток%10 % 31

💡 Деление целых чисел всегда возвращает целое число. Например, 5 / 2 = 2, а не 2.5. Для дробного результата нужно использовать float.

var a = 10
var b = 3
fmt.Println(a / b) // 3 (int)
fmt.Println(float64(a) / float64(b)) // 3.3333 (float)

✅ Операторы присваивания

Операторы присваивания — это удобный синтаксический сахар для модификации переменных. Они объединяют арифметическую операцию с присваиванием в одну инструкцию, что делает код более читаемым и менее подверженным ошибкам.

Эти операторы особенно полезны в циклах, при работе с аккумуляторами, счётчиками и при обновлении состояния объектов. Они также снижают вероятность ошибок, когда нужно модифицировать переменную на основе её текущего значения:

x := 5
x = x + 1  // Обычное присваивание | x = 6
x += 2     // Увеличить на 2 | x = 8
x -= 1     // Уменьшить на 1 | x = 7
x *= 3     // Умножить на 3 | x = 21
x /= 2     // Разделить на 2 | x = 10
x %= 3     // Остаток от деления на 3 | x = 1

🧠 Инкремент и декремент

x++ // увеличивает на 1 | эквевалетна записи x = x + 1
x-- // уменьшает на 1 | эквевалетна записи x = x - 1

⚠️ В Go эти операторы — отдельные инструкции. Нельзя писать y = x++, как в C++ или Java.


🔁 Сравнения (bool-выражения)

Операции сравнения — основа условной логики в программах. Они сравнивают два значения и возвращают булево значение (true или false). Эти операции используются в условных конструкциях (if, switch), циклах (for, while) и любых других местах, где нужно принять решение на основе данных.

Go поддерживает строгую типизацию, поэтому сравнивать можно только значения совместимых типов. Результат сравнения всегда имеет тип bool:

ОперацияПример
== Равноx == 10
!= Не равноx != y
< Меньшеa < b
> Большеa > b
<= Меньше или равноa <= b
>= Больше или равноa >= b

🔗 Логические операции

Логические операторы — это инструменты для создания сложных условий в программах. Они работают с булевыми значениями (true или false) и позволяют комбинировать простые условия в более сложные логические выражения.

Это особенно важно при создании условных конструкций, проверке прав доступа, валидации данных и принятии решений в программе. Логические операторы следуют законам булевой алгебры и используют принцип короткого замыкания — если результат уже можно определить по первому операнду, второй не вычисляется.

  • && — логическое И (AND)
    Выражение возвращает true, только если оба условия истинны.
    Пример: x > 5 && x < 10 вернёт true, если x больше 5 и меньше 10 одновременно.

  • || — логическое ИЛИ (OR)
    Выражение возвращает true, если хотя бы одно из условий истинно.
    Пример: x < 0 || x > 100 вернёт true, если x меньше 0 или больше 100.

  • ! — логическое НЕ (NOT)
    Инвертирует логическое значение.
    Пример: !isReady вернёт true, если isReady равно false.


📋 Таблица истинности

ABA && BA || B!A
truetruetruetruefalse
truefalsefalsetruefalse
falsetruefalsetruetrue
falsefalsefalsefalsetrue

📚 Операции со строками

Строки в Go поддерживают ограниченный набор операций по сравнению с числами. Основная операция — конкатенация (объединение строк), которая позволяет создавать новые строки из существующих. Это фундаментальная операция для формирования сообщений, путей к файлам, SQL-запросов и других текстовых структур.

Важно помнить, что строки в Go неизменяемы (immutable), поэтому каждая операция конкатенации создаёт новую строку в памяти:

first := "Hello, "
second := "world!"
greeting := first + second
fmt.Println(greeting) // Hello, world!

💡 В Go строки можно только складывать. Умножать или вычитать нельзя.


🚀 Практические примеры работы с операциями

Операции в Go — это не просто арифметические действия, но и мощные инструменты для решения реальных задач. Рассмотрим несколько примеров, которые показывают, как правильно использовать операции для создания надёжных программ.

Безопасный калькулятор с обработкой ошибок

В реальных проектах важно предусматривать обработку ошибок. Рассмотрим пример функции-калькулятора, которая корректно обрабатывает различные случаи, включая деление на ноль и неподдерживаемые операции:

func calculate(a, b float64, op string) (float64, error) {
    switch op {
    case "+": return a + b, nil
    case "-": return a - b, nil
    case "*": return a * b, nil
    case "/":
        if b == 0 { return 0, errors.New("деление на ноль") }
        return a / b, nil
    default: return 0, errors.New("неподдерживаемая операция")
    }
}

Такой подход делает код более надёжным и предсказуемым. Функция возвращает не только результат вычисления, но и потенциальную ошибку, что позволяет вызывающему коду правильно реагировать на проблемные ситуации. Это типичная практика в Go — всегда возвращать ошибку как второе значение.

Проверка силы пароля

func checkPasswordStrength(password string) string {
    var (
        hasUpper    = false
        hasLower    = false
        hasDigit    = false
        hasSpecial  = false
        length      = len(password)
    )

    for _, char := range password {
        switch {
        case char >= 'A' && char <= 'Z':
            hasUpper = true
        case char >= 'a' && char <= 'z':
            hasLower = true
        case char >= '0' && char <= '9':
            hasDigit = true
        case char == '!' || char == '@' || char == '#' || char == '$':
            hasSpecial = true
        }
    }

    score := 0
    if hasUpper { score++ }
    if hasLower { score++ }
    if hasDigit { score++ }
    if hasSpecial { score++ }
    if length >= 8 { score++ }

    switch {
    case score >= 5:
        return "🔒 Очень сильный"
    case score >= 3:
        return "🔑 Сильный"
    case score >= 2:
        return "⚠️ Средний"
    default:
        return "❌ Слабый"
    }
}

Операторы присваивания для счётчиков

Операторы присваивания (+=, -=, *=) особенно полезны при работе со счётчиками и аккумуляторами. Например, при подсчёте частоты слов в тексте:

// Подсчёт частоты слов
counter := make(map[string]int)
for _, word := range words {
    counter[word]++ // Короткая запись для counter[word] = counter[word] + 1
}

Такой подход делает код чище и понятнее. Оператор ++ автоматически обрабатывает случай, когда ключа ещё нет в карте (создаёт значение 0 и увеличивает его).


🧮 Побитовые операции (продвинутое)

📚 Примечание: Этот раздел предназначен для любознательных читателей. Побитовые операции редко используются в обычной разработке на Go, но полезны для понимания низкоуровневых оптимизаций.

🧮 Побитовые операции

Побитовые операции работают с целыми числами на уровне отдельных битов. Это может быть полезно, когда нужно экономить память, управлять флагами или взаимодействовать с низкоуровневыми структурами (например, сетевые протоколы, работа с оборудованием, сжатие данных и т.д.).

📋 Основные побитовые операторы

  • & — И (AND): устанавливает бит в 1, если он установлен в обоих операндах
  • | — ИЛИ (OR): устанавливает бит в 1, если он установлен хотя бы в одном операнде
  • ^ — исключающее ИЛИ (XOR): устанавливает бит в 1, если он установлен только в одном из операндов
  • &^ — очистка бита (AND NOT): обнуляет биты, установленные в правом операнде
  • << — сдвиг влево: сдвигает биты влево на указанное число позиций (умножение на 2ⁿ)
  • >> — сдвиг вправо: сдвигает биты вправо (деление на 2ⁿ)

🔍 Примеры

a := 12        // 1100 в двоичной системе
b := 10        // 1010

result1 := a & b   // 1000 = 8
result2 := a | b   // 1110 = 14
result3 := a ^ b   // 0110 = 6
result4 := a &^ b  // 0100 = 4
result5 := a << 1  // 11000 = 24
result6 := b >> 1  // 0101 = 5

🛠 Примеры использования

  • Флаги и маски

    const (
        FlagRead  = 1 << 0 // 0001
        FlagWrite = 1 << 1 // 0010
        FlagExec  = 1 << 2 // 0100
    )
    
    var perms uint8 = FlagRead | FlagWrite // 0011
    
    if perms&FlagWrite != 0 {
        fmt.Println("Запись разрешена")
    }
    
  • Оптимизация по памяти Побитовые флаги позволяют хранить до 8 независимых булевых значений в одном uint8.

  • Низкоуровневые вычисления Часто используются в криптографии, коде для микроконтроллеров и сетевых протоколов.


🧠 Побитовые операции — мощный инструмент, но применяются в основном в системном и высокопроизводительном коде. В повседневной разработке на Go они встречаются нечасто, но полезно понимать, как они работают.


🧪 Пример

package main

import "fmt"

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

    fmt.Println("x + y =", x + y)
    fmt.Println("x % y =", x % y)
    fmt.Println("x > y:", x > y)
    fmt.Println("x < 20 && y > 1:", x < 20 && y > 1)

    greeting := "Привет, " + "Go!"
    fmt.Println(greeting)

    // Побитовые операции (используются редко)
    fmt.Println("Побитовые:", x&y, x|y, x<<1)
}

🔍 Вопросы для самопроверки

  1. Чем отличается x++ от x += 1?
  2. Что произойдёт при делении 5 / 2?
  3. Можно ли использовать x = y++ в Go?
  4. Какие логические операторы ты знаешь?
  5. Как соединить две строки в Go?
  6. Как безопасно преобразовать int64 в int8?
  7. Когда могут понадобиться побитовые операции?

📌 Главное из главы

  • Операции в Go строго типизированы — нельзя складывать числа разных типов без явного преобразования
  • Арифметические операции работают с числами; деление целых чисел всегда даёт целое число
  • Логические операторы (&&, ||, !) используют короткое замыкание для оптимизации
  • Операторы присваивания (+=, -=, *=) делают код более читаемым и менее подверженным ошибкам
  • Инкремент/декремент (++, --) — отдельные инструкции, не возвращающие значения
  • Строковые операции ограничены конкатенацией; строки неизменяемы
  • Побитовые операции применяются в системном программировании и работе с флагами
  • Обработка ошибок критически важна для создания надёжных программ

🛠 Практические упражнения

Упражнение 1: Расширенный калькулятор

Создайте калькулятор с поддержкой всех базовых операций (+, -, *, /, %), включая обработку ошибок и сравнение целочисленного с дробным делением.

Упражнение 2: Анализатор текста и паролей

Реализуйте функцию анализа строк: подсчёт слов, поиск частых слов, проверка силы паролей с различными критериями безопасности.

Упражнение 3: Работа с операторами присваивания

Напишите программу для обработки массивов чисел с использованием +=, -=, *= для вычисления суммы, произведения и статистических показателей.