🐹 Go

गो में चर

Переменные и типы в Go: var, := и все типы данных

Переменные — это именованные ячейки памяти для хранения данных. В Go есть интересная особенность: два способа объявить переменную — через var и через оператор :=. И у каждого типа есть своё значение «по умолчанию» — zero value. Давай разберёмся!

Два способа объявить переменную

package main

import "fmt"

func main() {
    // Способ 1: var (явное объявление)
    var age int = 16
    var name string = "Алиса"
    var isStudent bool = true
    
    fmt.Println(age, name, isStudent)
    
    // Способ 2: := (краткое объявление, только внутри функций)
    score := 95           // int
    city := "Москва"     // string
    height := 1.75       // float64
    
    fmt.Println(score, city, height)
    
    // var без значения (будет zero value)
    var count int    // = 0
    var title string // = ""
    var active bool  // = false
    
    fmt.Println(count, title, active) // 0  false
}

Zero values: значения по умолчанию

В Go каждый тип имеет нулевое значение (zero value). Это как пустые коробки на складе: коробки есть, но они пустые. Ты знаешь, что там: числа — 0, строки — пусто, булевы — ложь.

package main

import "fmt"

func main() {
    var i int       // 0
    var f float64   // 0.0
    var s string    // "" (пустая строка)
    var b bool      // false
    var p *int      // nil (нулевой указатель)
    
    fmt.Printf("int: %d\n", i)
    fmt.Printf("float64: %f\n", f)
    fmt.Printf("string: %q\n", s) // %q добавляет кавычки
    fmt.Printf("bool: %t\n", b)
    fmt.Printf("pointer: %v\n", p)
    // int: 0
    // float64: 0.000000
    // string: ""
    // bool: false
    // pointer: 
}

Числовые типы

package main

import "fmt"

func main() {
    // Целые числа
    var a int8  = 127          // -128 до 127
    var b int16 = 32767        // ~±32 тыс
    var c int32 = 2147483647   // ~±2 млрд
    var d int64 = 9223372036854775807 // ~±9×10^18
    
    // int — зависит от платформы (32 или 64 бита, обычно 64)
    var e int = 42
    
    // Беззнаковые
    var f uint = 100  // только положительные
    
    // Числа с плавающей точкой
    var g float32 = 3.14           // 7 значимых цифр
    var h float64 = 3.14159265358979 // 15-17 значимых цифр
    
    // Комплексные числа (редко нужны)
    var k complex128 = 3 + 4i
    
    fmt.Println(a, b, c, d, e, f)
    fmt.Println(g, h)
    fmt.Println(k) // (3+4i)
    
    // Математика
    x := 17
    y := 5
    fmt.Println(x + y)  // 22
    fmt.Println(x - y)  // 12
    fmt.Println(x * y)  // 85
    fmt.Println(x / y)  // 3  (целочисленное деление!)
    fmt.Println(x % y)  // 2  (остаток)
}

Строки и символы

package main

import (
    "fmt"
    "strings"
)

func main() {
    // Строки в двойных кавычках
    name := "Привет, мир!"
    
    // Длина строки (в байтах!)
    fmt.Println(len(name)) // 23 для UTF-8 (русские буквы — 2 байта)
    
    // Конкатенация
    firstName := "Иван"
    lastName := "Петров"
    fullName := firstName + " " + lastName
    fmt.Println(fullName) // Иван Петров
    
    // Методы строк
    fmt.Println(strings.ToUpper(name))         // ПРИВЕТ, МИР!
    fmt.Println(strings.ToLower(name))         // привет, мир!
    fmt.Println(strings.Contains(name, "мир")) // true
    fmt.Println(strings.Replace(name, "мир", "Go", 1)) // Привет, Go!
    fmt.Println(strings.Split("a,b,c", ","))   // [a b c]
    
    // Многострочная строка (backtick)
    multiline := `Строка 1
Строка 2
Строка 3`
    fmt.Println(multiline)
    
    // Rune — для работы с Unicode символами
    text := "Привет"
    runes := []rune(text)
    fmt.Println(len(runes)) // 6 (6 символов, а не байтов)
}

Тип bool и операции

package main

import "fmt"

func main() {
    isRaining := false
    isCold := true
    
    // Логические операции
    fmt.Println(isRaining && isCold)  // false (И)
    fmt.Println(isRaining || isCold)  // true  (ИЛИ)
    fmt.Println(!isRaining)           // true  (НЕ)
    
    // Сравнения возвращают bool
    a, b := 10, 5
    fmt.Println(a == b)  // false
    fmt.Println(a != b)  // true
    fmt.Println(a > b)   // true
    fmt.Println(a < b)   // false
    fmt.Println(a >= b)  // true
    fmt.Println(a <= b)  // false
}

Множественное присваивание

package main

import "fmt"

func main() {
    // Несколько переменных в одной строке
    x, y := 10, 20
    fmt.Println(x, y) // 10 20
    
    // Обмен переменных — элегантно!
    x, y = y, x
    fmt.Println(x, y) // 20 10
    
    // Объявление нескольких var
    var (
        name    string = "Алиса"
        age     int    = 20
        isAdult bool   = true
    )
    fmt.Println(name, age, isAdult)
    
    // Discard — игнорировать значение
    a, _ := 10, 20  // _ означает "мне не нужно это значение"
    fmt.Println(a)  // 10
}

Константы

package main

import "fmt"

// Константы объявляются с const
const Pi = 3.14159265358979
const MaxPlayers = 100
const AppName = "МояИгра"

// Группа констант
const (
    StatusOK    = 200
    StatusError = 500
    AppVersion  = "1.0.0"
)

func main() {
    // Нельзя изменить константу
    // Pi = 3.14 // ОШИБКА: cannot assign to Pi

    fmt.Printf("Площадь круга r=5: %.2f\n", Pi * 5 * 5)
    fmt.Printf("Максимум игроков: %d\n", MaxPlayers)
}

iota: автоматические счётчики для enum

package main

import "fmt"

// iota — автоинкремент для констант (аналог enum)
type Direction int

const (
    North Direction = iota // 0
    East                   // 1
    South                  // 2
    West                   // 3
)

type Day int

const (
    Monday Day = iota + 1 // 1 (iota=0, +1=1)
    Tuesday               // 2
    Wednesday             // 3
    Thursday              // 4
    Friday                // 5
    Saturday              // 6
    Sunday                // 7
)

// Степени двойки с iota
const (
    KB = 1 << (10 * (iota + 1)) // 1024
    MB                           // 1048576
    GB                           // 1073741824
)

func main() {
    dir := North
    fmt.Println(dir) // 0
    
    fmt.Println(Monday, Tuesday, Wednesday) // 1 2 3
    fmt.Printf("KB=%d, MB=%d, GB=%d\n", KB, MB, GB)
    // KB=1024, MB=1048576, GB=1073741824
    
    // Использование в switch
    day := Wednesday
    switch day {
    case Monday, Tuesday, Wednesday, Thursday, Friday:
        fmt.Println("Рабочий день")
    case Saturday, Sunday:
        fmt.Println("Выходной!")
    }
}

Преобразование типов

package main

import (
    "fmt"
    "strconv"
)

func main() {
    // В Go нет неявного преобразования! Только явное
    var i int = 42
    var f float64 = float64(i) // явное преобразование
    var u uint = uint(f)
    
    fmt.Println(i, f, u) // 42 42 42
    
    // ОШИБКА без преобразования:
    // var x float64 = 42 + i  // mismatched types
    
    // Строка ↔ число
    numStr := "123"
    num, err := strconv.Atoi(numStr) // string → int
    if err != nil {
        fmt.Println("Ошибка:", err)
    } else {
        fmt.Println(num + 1) // 124
    }
    
    str := strconv.Itoa(456)  // int → string
    fmt.Println(str + "!")    // 456!
    
    // float string
    f2, _ := strconv.ParseFloat("3.14", 64)
    fmt.Println(f2) // 3.14
}

Итого

  • ✅ var — явное объявление переменной (можно использовать везде)
  • ✅ := — краткое объявление (только внутри функций, определяет тип автоматически)
  • ✅ Zero values — все переменные инициализируются по умолчанию: 0, "", false, nil
  • ✅ int, float64, string, bool — основные типы данных
  • ✅ const — константа, нельзя изменить
  • ✅ iota — автоматический счётчик для группы констант (аналог enum)
  • ✅ В Go нет неявного преобразования типов — всегда нужно явно: float64(x)

Тип rune: работа с Unicode

package main

import "fmt"

func main() {
    // string — это байты, а не символы!
    s := "Привет"
    fmt.Println(len(s))       // 12 (байт, не символов — кириллица 2 байта)
    
    // rune — это int32, представляет Unicode code point
    runes := []rune(s)
    fmt.Println(len(runes))   // 6 (символов)
    
    // Правильный перебор символов
    for i, r := range s {
        fmt.Printf("index=%d char=%c code=%d\n", i, r, r)
    }
    
    // Срезы строки по символам (не байтам)
    r := []rune(s)
    fmt.Println(string(r[0:3]))  // "При"
}

Тип byte: работа с бинарными данными

package main

import "fmt"

func main() {
    // byte = uint8 = число от 0 до 255
    var b byte = 255
    
    // Строка — это []byte в Go
    s := "Hello"
    bytes := []byte(s)
    fmt.Println(bytes)  // [72 101 108 108 111]
    
    // Изменение строки через байты
    bytes[0] = 'h'
    fmt.Println(string(bytes))  // "hello"
    
    // Полезно при работе с сетью, файлами, криптографией
}

Указатели в Go

package main

import "fmt"

func main() {
    x := 42
    
    // & — адрес переменной (получить указатель)
    p := &x
    fmt.Println(p)   // 0xc0000b4008 (адрес в памяти)
    fmt.Println(*p)  // 42 (разыменование — значение по адресу)
    
    // Изменение через указатель
    *p = 100
    fmt.Println(x)   // 100 — x изменился!
    
    // new — выделить память и вернуть указатель
    n := new(int)  // *int, значение 0
    *n = 55
    fmt.Println(*n)  // 55
    
    // Указатели на struct (часто используется)
    type Point struct{ X, Y int }
    pt := &Point{X: 3, Y: 4}
    pt.X = 10  // Go автоматически разыменовывает: (*pt).X = 10
    fmt.Println(pt)  // &{10 4}
}

Массивы и срезы

package main

import "fmt"

func main() {
    // Массив — фиксированный размер
    var arr [5]int = [5]int{1, 2, 3, 4, 5}
    arr2 := [3]string{"Go", "Python", "Rust"}
    arr3 := [...]int{10, 20, 30}  // ... = компилятор считает размер
    
    fmt.Println(arr[0])      // 1
    fmt.Println(len(arr))    // 5
    
    // Срез (slice) — динамический массив, ОСНОВНАЯ коллекция в Go
    s := []int{1, 2, 3, 4, 5}
    s2 := make([]int, 5)        // [0 0 0 0 0]
    s3 := make([]int, 3, 10)    // длина 3, ёмкость 10
    
    fmt.Println(len(s), cap(s)) // 5, 5
    
    // Добавление элементов
    s = append(s, 6, 7, 8)
    fmt.Println(s)  // [1 2 3 4 5 6 7 8]
    
    // Срезы среза
    fmt.Println(s[1:4])  // [2 3 4]
    fmt.Println(s[:3])   // [1 2 3]
    fmt.Println(s[3:])   // [4 5 6 7 8]
    
    // Копирование
    dst := make([]int, len(s))
    copy(dst, s)
    
    _ = s2; _ = s3; _ = arr2; _ = arr3; _ = dst
}

Map: словарь ключ-значение

package main

import "fmt"

func main() {
    // Создание map
    ages := map[string]int{
        "Алиса": 20,
        "Борис": 25,
        "Вера":  22,
    }
    
    // Доступ к значению
    fmt.Println(ages["Алиса"])  // 20
    
    // Проверка существования ключа
    age, ok := ages["Гриша"]
    if ok {
        fmt.Println("Возраст:", age)
    } else {
        fmt.Println("Гриша не найден")
    }
    
    // Добавление и изменение
    ages["Гриша"] = 19
    ages["Алиса"] = 21  // Обновляем
    
    // Удаление
    delete(ages, "Борис")
    
    // Перебор (порядок не гарантирован!)
    for name, age := range ages {
        fmt.Printf("%s: %d лет\n", name, age)
    }
    
    fmt.Println("Всего:", len(ages))
    
    // make для создания map
    scores := make(map[string][]int)
    scores["Алиса"] = []int{85, 92, 78}
    scores["Борис"] = append(scores["Борис"], 75)
    fmt.Println(scores)
}

Итого

  • ✅ var — явное объявление переменной
  • ✅ := — краткое объявление (только внутри функций)
  • ✅ Zero values: int=0, float=0.0, string="", bool=false, pointer=nil
  • ✅ int, float64, string, bool — основные типы
  • ✅ rune (int32) — символ Unicode; byte (uint8) — байт
  • ✅ const — неизменяемое значение
  • ✅ iota — автоинкремент для группы констант (аналог enum)
  • ✅ В Go нет неявных преобразований — только явные: float64(x)
  • ✅ []T (срез) — основная коллекция в Go
  • ✅ map[K]V — словарь ключ-значение

Работа с числами: стандартные операции

package main

import (
    "fmt"
    "math"
)

func main() {
    // Математические операции
    a, b := 17, 5
    fmt.Println(a + b)   // 22
    fmt.Println(a - b)   // 12
    fmt.Println(a * b)   // 85
    fmt.Println(a / b)   // 3 (целочисленное!)
    fmt.Println(a % b)   // 2 (остаток)
    
    // Операторы присваивания
    x := 10
    x += 5   // 15
    x -= 3   // 12
    x *= 2   // 24
    x /= 4   // 6
    x %= 4   // 2
    fmt.Println(x)
    
    // Инкремент/декремент (только постфиксный в Go!)
    i := 5
    i++  // i = 6
    i--  // i = 5
    // В Go нет ++i или --i (только i++ и i--)
    
    // Математические функции
    fmt.Printf("sqrt(16) = %.0f\n", math.Sqrt(16))   // 4
    fmt.Printf("pow(2,8) = %.0f\n", math.Pow(2, 8))  // 256
    fmt.Printf("abs(-42) = %.0f\n", math.Abs(-42))    // 42
    fmt.Printf("floor(3.7) = %.0f\n", math.Floor(3.7)) // 3
    fmt.Printf("ceil(3.2) = %.0f\n", math.Ceil(3.2))   // 4
    fmt.Printf("round(3.5) = %.0f\n", math.Round(3.5)) // 4
}

Практика: конвертер температур

package main

import "fmt"

func celsiusToFahrenheit(c float64) float64 { return c*9/5 + 32 }
func fahrenheitToCelsius(f float64) float64 { return (f - 32) * 5 / 9 }
func celsiusToKelvin(c float64) float64     { return c + 273.15 }

func main() {
    temperatures := []float64{-40, 0, 20, 37, 100}
    
    fmt.Printf("%-8s %-12s %-12s\n", "Цельсий", "Фаренгейт", "Кельвин")
    fmt.Println(fmt.Sprintf("%s", "─────────────────────────────────"))
    
    for _, c := range temperatures {
        fmt.Printf("%-8.1f %-12.1f %-12.2f\n",
            c,
            celsiusToFahrenheit(c),
            celsiusToKelvin(c))
    }
}

Форматирование и вывод разных типов

package main

import "fmt"

func main() {
    // Типы и их zero values
    var i int; var f float64; var s string; var b bool
    fmt.Printf("int: %d, float64: %f, string: %q, bool: %t\n", i, f, s, b)
    // int: 0, float64: 0.000000, string: "", bool: false
    
    // Таблица форматирования числа
    n := 255
    fmt.Printf("Десятичное: %d\n", n)     // 255
    fmt.Printf("Двоичное:   %b\n", n)     // 11111111
    fmt.Printf("Восьмеричное: %o\n", n)   // 377
    fmt.Printf("Шестнадц.: %x\n", n)      // ff
    fmt.Printf("Шестнадц.: %X\n", n)      // FF
    fmt.Printf("Символ:    %c\n", 65)     // A
    
    // Выравнивание
    fmt.Printf("|%10d|\n", n)     // |       255|  (право)
    fmt.Printf("|%-10d|\n", n)    // |255       |  (лево)
    fmt.Printf("|%010d|\n", n)    // |0000000255|  (нули)
    fmt.Printf("|%+d|\n", n)      // |+255|  (знак)
    
    // Числа с плавающей точкой
    pi := 3.14159265358979
    fmt.Printf("%.2f\n", pi)   // 3.14
    fmt.Printf("%.5f\n", pi)   // 3.14159
    fmt.Printf("%e\n", pi)     // 3.141593e+00
    fmt.Printf("%g\n", pi)     // 3.14159265358979
}

Итого

  • ✅ var и := — два способа объявить переменную
  • ✅ Zero values — переменные инициализируются по умолчанию
  • ✅ iota — автоинкремент для констант (аналог enum)
  • ✅ Явные преобразования типов обязательны в Go
  • ✅ []T — срез (динамический массив), map[K]V — словарь