461 lines
22 KiB
Go
Executable file
461 lines
22 KiB
Go
Executable file
package gomodules
|
||
|
||
import (
|
||
"fmt"
|
||
"rubric/utils"
|
||
)
|
||
|
||
var StructuresModule = Module{
|
||
Key: "structs",
|
||
Aliases: []string{"s", "struct", "vars", "types"},
|
||
Title: "Структура программы",
|
||
Description: "Имена, объявления, переменные, указатели, типы, пакеты, область видимости",
|
||
Run: showStructures,
|
||
}
|
||
|
||
func showStructures() {
|
||
utils.Header("Go — Структура программы")
|
||
|
||
// ─── Имена и ключевые слова ───────────────────────────────────
|
||
utils.Section("Имена и ключевые слова")
|
||
|
||
utils.Println("Имя начинается с буквы (Unicode) или символа подчёркивания, затем — буквы, цифры, _.")
|
||
utils.Println("Регистр имеет значение: " +
|
||
utils.ColorBold + utils.ColorCyan + "Name" + utils.ColorReset +
|
||
" и " +
|
||
utils.ColorBold + utils.ColorCyan + "name" + utils.ColorReset +
|
||
" — разные идентификаторы.")
|
||
fmt.Println()
|
||
|
||
utils.Println(utils.ColorBold + utils.ColorYellow + "25 ключевых слов языка Go:" + utils.ColorReset)
|
||
fmt.Println()
|
||
keywords := [][]string{
|
||
{"break", "case", "chan", "const", "continue"},
|
||
{"default", "defer", "else", "fallthrough", "for"},
|
||
{"func", "go", "goto", "if", "import"},
|
||
{"interface", "map", "package", "range", "return"},
|
||
{"select", "struct", "switch", "type", "var"},
|
||
}
|
||
for _, row := range keywords {
|
||
fmt.Print(" ")
|
||
for _, kw := range row {
|
||
fmt.Printf("%s%-14s%s", utils.ColorCyan+utils.ColorBold, kw, utils.ColorReset)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
fmt.Println()
|
||
|
||
utils.Println(utils.ColorBold + utils.ColorYellow + "Предопределённые имена (не ключевые слова — можно переопределить, но не стоит):" + utils.ColorReset)
|
||
fmt.Println()
|
||
utils.Println(utils.ColorDim + " Типы:" + utils.ColorReset)
|
||
utils.Println(" bool byte rune string error")
|
||
utils.Println(" int int8 int16 int32 int64")
|
||
utils.Println(" uint uint8 uint16 uint32 uint64 uintptr")
|
||
utils.Println(" float32 float64 complex64 complex128")
|
||
fmt.Println()
|
||
utils.Println(utils.ColorDim + " Константы:" + utils.ColorReset)
|
||
utils.Println(" true false iota")
|
||
fmt.Println()
|
||
utils.Println(utils.ColorDim + " Нулевое значение:" + utils.ColorReset)
|
||
utils.Println(" nil")
|
||
fmt.Println()
|
||
utils.Println(utils.ColorDim + " Встроенные функции:" + utils.ColorReset)
|
||
utils.Println(" append cap close complex copy delete imag")
|
||
utils.Println(" len make new panic print println real recover")
|
||
fmt.Println()
|
||
utils.Note("Если имя начинается с заглавной буквы — оно экспортируется из пакета (публичное).")
|
||
utils.Note("Стиль: camelCase для локальных, CamelCase для экспортируемых. Аббревиатуры — заглавными: URL, ID, HTTP.")
|
||
|
||
// ─── Объявления ───────────────────────────────────────────────
|
||
utils.Section("Объявления")
|
||
|
||
utils.Println("В Go четыре вида объявлений верхнего уровня:")
|
||
fmt.Println()
|
||
utils.Code(`package main
|
||
|
||
import "fmt"
|
||
|
||
// const — константа верхнего уровня
|
||
const Greeting = "Привет"
|
||
|
||
// type — новый тип
|
||
type Celsius float64
|
||
|
||
// var — переменная верхнего уровня
|
||
var boilingPoint Celsius = 100
|
||
|
||
// func — функция
|
||
func main() {
|
||
fmt.Println(Greeting, boilingPoint)
|
||
}`)
|
||
utils.Println("Порядок объявлений на верхнем уровне не важен — компилятор разрешает зависимости сам.")
|
||
|
||
// ─── Переменные ───────────────────────────────────────────────
|
||
utils.Section("Переменные и способы объявления")
|
||
|
||
utils.Println(utils.ColorBold + "1. Полное объявление через var:" + utils.ColorReset)
|
||
utils.Code(`var имя тип = выражение
|
||
|
||
var x int // x == 0 (нулевое значение)
|
||
var s string // s == ""
|
||
var b bool // b == false
|
||
var p *int // p == nil
|
||
|
||
// Несколько переменных одного типа:
|
||
var i, j, k int
|
||
|
||
// Несколько переменных разных типов:
|
||
var (
|
||
name string = "Gopher"
|
||
age int = 3
|
||
active bool = true
|
||
)`)
|
||
|
||
utils.Println(utils.ColorBold + "2. Краткое объявление := (только внутри функций):" + utils.ColorReset)
|
||
utils.Code(`имя := выражение // тип выводится автоматически
|
||
|
||
x := 42 // int
|
||
s := "hello" // string
|
||
f := 3.14 // float64
|
||
b := true // bool
|
||
|
||
// Несколько переменных:
|
||
i, j := 0, 1
|
||
name, err := os.Open("file.txt") // часто используется для ошибок
|
||
|
||
// ВАЖНО: хотя бы одна переменная слева должна быть новой:
|
||
x, y := 1, 2 // ok: обе новые
|
||
x, y = 3, 4 // ok: присваивание (уже объявлены)
|
||
x, z := 5, 6 // ok: z — новая, x — переприсваивается`)
|
||
|
||
utils.Println(utils.ColorBold + "3. Нулевые значения (zero values):" + utils.ColorReset)
|
||
utils.Code(`// Каждый тип имеет нулевое значение при объявлении без инициализации:
|
||
var i int // 0
|
||
var f float64 // 0.0
|
||
var b bool // false
|
||
var s string // ""
|
||
var p *int // nil
|
||
var sl []int // nil
|
||
var m map[string]int // nil
|
||
var fn func() // nil
|
||
var err error // nil`)
|
||
utils.Note("В Go нет неинициализированных переменных — нулевое значение гарантировано.")
|
||
|
||
utils.Println(utils.ColorBold + "4. Объявление нескольких переменных с var-блоком:" + utils.ColorReset)
|
||
utils.Code(`// Группировка для читаемости:
|
||
var (
|
||
debug = false
|
||
version = "1.0.0"
|
||
maxConn = 100
|
||
)`)
|
||
|
||
// ─── Указатели ────────────────────────────────────────────────
|
||
utils.Section("Указатели")
|
||
|
||
utils.Println("Указатель хранит " + utils.ColorBold + "адрес" + utils.ColorReset + " переменной в памяти.")
|
||
utils.Code(`var x int = 42
|
||
|
||
p := &x // p — указатель на x, тип *int
|
||
fmt.Println(p) // 0xc0000b4008 (адрес)
|
||
fmt.Println(*p) // 42 (разыменование — значение по адресу)
|
||
|
||
*p = 100 // изменить значение x через указатель
|
||
fmt.Println(x) // 100
|
||
|
||
// Указатель на указатель:
|
||
pp := &p // тип **int
|
||
fmt.Println(**pp) // 100`)
|
||
|
||
utils.Code(`// Нулевое значение указателя — nil:
|
||
var p *int
|
||
fmt.Println(p == nil) // true
|
||
// *p = 1 — паника! нельзя разыменовывать nil-указатель
|
||
|
||
// Сравнение: указатели равны, если указывают на один адрес:
|
||
a, b := 1, 1
|
||
pa, pb := &a, &b
|
||
fmt.Println(pa == pb) // false (разные переменные)
|
||
pa2 := &a
|
||
fmt.Println(pa == pa2) // true (один и тот же адрес)`)
|
||
|
||
utils.Note("Go — безопасный язык: нет арифметики указателей (без пакета unsafe).")
|
||
utils.Note("Указатель позволяет функции изменять переменную вызывающего кода.")
|
||
|
||
// ─── Функция new ──────────────────────────────────────────────
|
||
utils.Section("Функция new")
|
||
|
||
utils.Println("new(T) выделяет память для значения типа T, инициализирует нулевым значением,")
|
||
utils.Println("возвращает указатель *T.")
|
||
utils.Code(`p := new(int) // *int, *p == 0
|
||
fmt.Println(*p) // 0
|
||
*p = 7
|
||
fmt.Println(*p) // 7
|
||
|
||
// Эквивалентно:
|
||
var tmp int
|
||
p2 := &tmp
|
||
|
||
// new для структур:
|
||
type Point struct{ X, Y int }
|
||
pt := new(Point) // *Point, pt.X==0, pt.Y==0
|
||
pt.X = 10`)
|
||
|
||
utils.Note("new используется редко — чаще пишут &T{...} для структур или var x T; return &x.")
|
||
utils.Code(`// Эти три записи эквивалентны:
|
||
p1 := new(int)
|
||
|
||
var v int
|
||
p2 := &v
|
||
|
||
p3 := &struct{ int }{} // анонимная структура — другой случай`)
|
||
|
||
// ─── Время жизни переменных ───────────────────────────────────
|
||
utils.Section("Время жизни переменных")
|
||
|
||
utils.Println("Время жизни — период, в течение которого переменная существует в памяти.")
|
||
fmt.Println()
|
||
utils.Println(utils.ColorBold + "Переменные пакетного уровня:" + utils.ColorReset +
|
||
" живут всё время работы программы.")
|
||
utils.Println(utils.ColorBold + "Локальные переменные:" + utils.ColorReset +
|
||
" создаются при каждом выполнении объявления, удаляются когда становятся недостижимы.")
|
||
fmt.Println()
|
||
utils.Code(`// Escape analysis — компилятор решает, где хранить переменную:
|
||
// стек (быстро) или куча (heap, медленнее, но живёт дольше).
|
||
|
||
func newInt() *int {
|
||
v := 42 // v "убегает" на кучу (heap escape),
|
||
return &v // потому что возвращается её адрес.
|
||
}
|
||
|
||
func sum(a, b int) int {
|
||
result := a + b // result на стеке — не "убегает",
|
||
return result // возвращается значение, не адрес.
|
||
}`)
|
||
utils.Note("Не нужно думать о стеке/куче вручную — GC Go управляет памятью автоматически.")
|
||
utils.Note("go build -gcflags='-m' — показывает, какие переменные уходят на кучу.")
|
||
|
||
// ─── Присваивания ─────────────────────────────────────────────
|
||
utils.Section("Присваивания")
|
||
|
||
utils.Println(utils.ColorBold + "Простое присваивание:" + utils.ColorReset)
|
||
utils.Code(`x = 1
|
||
*p = true
|
||
person.name = "Алиса"
|
||
count[x] = count[x] * scale`)
|
||
|
||
utils.Println(utils.ColorBold + "Составные операторы присваивания:" + utils.ColorReset)
|
||
utils.Code(`x += 1 x -= 1 x *= 2 x /= 2 x %= 3
|
||
x &= y x |= y x ^= y x <<= 2 x >>= 1`)
|
||
|
||
utils.Println(utils.ColorBold + "Инкремент и декремент (только постфиксные, только как операторы):" + utils.ColorReset)
|
||
utils.Code(`x++ // x = x + 1
|
||
x-- // x = x - 1
|
||
|
||
// ВАЖНО: в Go нет префиксных ++/-- и нет выражений с ++/--:
|
||
// ++x — ошибка компиляции
|
||
// y = x++ — ошибка компиляции`)
|
||
|
||
utils.Println(utils.ColorBold + "Множественное присваивание (tuple assignment):" + utils.ColorReset)
|
||
utils.Code(`// Все правые части вычисляются ДО записи в левые:
|
||
x, y = y, x // обмен значениями
|
||
|
||
a, b, c := 1, 2, 3
|
||
a, b, c = b, c, a // сдвиг
|
||
|
||
// Возврат нескольких значений из функции:
|
||
quotient, remainder := divide(17, 5)
|
||
|
||
// Игнорирование значения через _:
|
||
_, err = fmt.Println("hi")`)
|
||
|
||
utils.Println(utils.ColorBold + "Присваиваемость (assignability):" + utils.ColorReset)
|
||
utils.Code(`// Присваивание допустимо, если:
|
||
// 1. Типы идентичны
|
||
// 2. Типы имеют одинаковую базу и хотя бы один — безымянный
|
||
// 3. T реализует интерфейс I
|
||
// 4. Присваивание nil совместимому типу (chan, func, interface, map, pointer, slice)
|
||
|
||
var i interface{} = "строка" // string → interface{}
|
||
var n int = 42
|
||
var f float64 = float64(n) // явное преобразование типов`)
|
||
|
||
// ─── Объявления типов ────────────────────────────────────────
|
||
utils.Section("Объявления типов")
|
||
|
||
utils.Println("type создаёт " + utils.ColorBold + "новый именованный тип" + utils.ColorReset + " с тем же базовым представлением.")
|
||
utils.Code(`type Celsius float64
|
||
type Fahrenheit float64
|
||
|
||
var boiling Celsius = 100
|
||
|
||
// Celsius и Fahrenheit — РАЗНЫЕ типы, хотя оба float64:
|
||
// boiling = Fahrenheit(212) — ошибка компиляции!
|
||
|
||
// Явное преобразование разрешено (одно базовое представление):
|
||
f := Fahrenheit(boiling*9/5 + 32)
|
||
fmt.Println(f) // 212`)
|
||
|
||
utils.Code(`// Методы можно добавлять только к именованным типам:
|
||
func (c Celsius) String() string {
|
||
return fmt.Sprintf("%.2f°C", float64(c))
|
||
}
|
||
|
||
fmt.Println(boiling) // 100.00°C (автоматически вызывается String())`)
|
||
|
||
utils.Code(`// type-алиас (псевдоним) — НЕ создаёт новый тип, это то же самое:
|
||
type Rune = int32 // rune — именно так определён в builtin
|
||
|
||
// Отличие: алиас и оригинал взаимозаменяемы без конверсии.
|
||
// Обычный type — нет.`)
|
||
|
||
utils.Note("type позволяет добавлять методы к базовым типам (int, string, slice и т.д.).")
|
||
utils.Note("Явное преобразование T(v) работает если оба типа имеют одно представление.")
|
||
|
||
// ─── Пакеты, файлы и импорт ──────────────────────────────────
|
||
utils.Section("Пакеты, файлы и импорт")
|
||
|
||
utils.Println("Пакет — единица инкапсуляции и переиспользования в Go.")
|
||
utils.Println("Все .go-файлы в одной директории — один пакет. Имя объявляется в первой строке.")
|
||
fmt.Println()
|
||
utils.Code(`// Каждый файл начинается с объявления пакета:
|
||
package main // исполняемая программа
|
||
package utils // библиотечный пакет
|
||
package http // пакет стандартной библиотеки`)
|
||
|
||
utils.Println(utils.ColorBold + "Импорт пакетов:" + utils.ColorReset)
|
||
utils.Code(`// Одиночный импорт:
|
||
import "fmt"
|
||
|
||
// Групповой импорт (предпочтительный стиль):
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"strings"
|
||
|
||
"github.com/user/project/utils" // внешний пакет
|
||
)`)
|
||
|
||
utils.Println(utils.ColorBold + "Формы импорта:" + utils.ColorReset)
|
||
utils.Code(`import "fmt" // стандартный: fmt.Println(...)
|
||
import f "fmt" // псевдоним: f.Println(...)
|
||
import . "fmt" // слияние: Println(...) (не рекомендуется)
|
||
import _ "image/png" // только для side-эффектов (init)`)
|
||
|
||
utils.Note("Неиспользуемый импорт — ошибка компиляции. Используйте _ для подавления.")
|
||
utils.Note("Имя пакета и имя директории могут отличаться, но по соглашению совпадают.")
|
||
utils.Code(`// Путь импорта vs имя пакета:
|
||
import "math/rand" // путь: math/rand
|
||
rand.Intn(10) // имя пакета: rand
|
||
|
||
import "crypto/rand" // другой пакет, то же имя!
|
||
// Нужен алиас:
|
||
import (
|
||
mrand "math/rand"
|
||
crand "crypto/rand"
|
||
)`)
|
||
|
||
// ─── Инициализация пакетов ────────────────────────────────────
|
||
utils.Section("Инициализация пакетов")
|
||
|
||
utils.Println("Инициализация переменных пакетного уровня происходит в порядке объявления,")
|
||
utils.Println("с учётом зависимостей между ними.")
|
||
utils.Code(`// Порядок гарантирован: a, b, c
|
||
var a = 1
|
||
var b = a + 1 // 2
|
||
var c = b * 2 // 4
|
||
|
||
// Сложные случаи — через функцию init():
|
||
var lookup = buildLookup()
|
||
|
||
func buildLookup() map[string]int {
|
||
// ...
|
||
return m
|
||
}`)
|
||
|
||
utils.Println(utils.ColorBold + "Функция init():" + utils.ColorReset)
|
||
utils.Code(`// init — специальная функция инициализации пакета:
|
||
// - вызывается автоматически при запуске программы
|
||
// - нельзя вызвать явно
|
||
// - в одном файле может быть несколько init()
|
||
// - в одном пакете — несколько init() в разных файлах
|
||
|
||
func init() {
|
||
// подготовка состояния, регистрация, настройка
|
||
log.SetFlags(log.Lshortfile)
|
||
}`)
|
||
|
||
utils.Println(utils.ColorBold + "Порядок инициализации всей программы:" + utils.ColorReset)
|
||
utils.Code(`// 1. Инициализируются импортируемые пакеты (рекурсивно, от листьев к корню)
|
||
// 2. Переменные пакетного уровня в порядке объявления / зависимостей
|
||
// 3. Вызываются все функции init() данного пакета
|
||
// 4. ... переход к следующему пакету
|
||
// 5. Последним инициализируется пакет main
|
||
// 6. Вызывается main.main()
|
||
|
||
// import _ "pkg" — импортировать только ради init()
|
||
// Пример: регистрация драйвера БД:
|
||
import _ "github.com/lib/pq" // PostgreSQL driver`)
|
||
|
||
utils.Note("Порядок вызова init() между файлами одного пакета определяется алфавитным порядком имён файлов.")
|
||
|
||
// ─── Область видимости ───────────────────────────────────────
|
||
utils.Section("Область видимости (Scope)")
|
||
|
||
utils.Println("Область видимости — часть кода, в которой объявление действует.")
|
||
utils.Println("Это " + utils.ColorBold + "статическое" + utils.ColorReset + " свойство (на уровне компиляции), в отличие от времени жизни (runtime).")
|
||
fmt.Println()
|
||
utils.Code(`// Уровни области видимости (от широкого к узкому):
|
||
// 1. Вселенная (universe) — встроенные имена: int, len, nil...
|
||
// 2. Пакет — объявления на верхнем уровне файла
|
||
// 3. Файл — только import-пути
|
||
// 4. Локальный — блок { }, параметры функции, for, if, switch...
|
||
|
||
var x = "пакет" // область видимости — весь пакет
|
||
|
||
func f() {
|
||
fmt.Println(x) // "пакет"
|
||
x := "локальный" // новая переменная x (shadowing!)
|
||
fmt.Println(x) // "локальный"
|
||
{
|
||
x := "вложенный"
|
||
fmt.Println(x) // "вложенный"
|
||
}
|
||
fmt.Println(x) // "локальный"
|
||
}`)
|
||
|
||
utils.Println(utils.ColorBold + "Затенение (shadowing) в управляющих конструкциях:" + utils.ColorReset)
|
||
utils.Code(`// if-инициализатор:
|
||
if x := compute(); x > 0 {
|
||
// x видна только здесь...
|
||
} else {
|
||
// ...и здесь
|
||
}
|
||
// x здесь уже не видна
|
||
|
||
// Распространённая ошибка с := в if:
|
||
var err error
|
||
if err := doA(); err != nil { // новая err!
|
||
return err
|
||
}
|
||
// err (внешняя) всё ещё nil — doA не изменил её!
|
||
fmt.Println(err) // nil`)
|
||
|
||
utils.Println(utils.ColorBold + "Экспортируемые имена — публичный API пакета:" + utils.ColorReset)
|
||
utils.Code(`package geometry
|
||
|
||
// Экспортируется (заглавная буква):
|
||
type Point struct {
|
||
X, Y float64 // поля тоже экспортируются
|
||
}
|
||
|
||
func Distance(p, q Point) float64 { ... }
|
||
|
||
// НЕ экспортируется (строчная буква):
|
||
type circle struct { center Point; radius float64 }
|
||
func area(c circle) float64 { ... }`)
|
||
|
||
utils.Note("Экспортируемость — единственный механизм инкапсуляции в Go. Нет private/protected/public ключевых слов.")
|
||
utils.Note("Правило: экспортируй только то, что является частью публичного API. Всё остальное — строчными буквами.")
|
||
|
||
utils.Footer()
|
||
}
|