Added go composite
This commit is contained in:
parent
0d3dae172a
commit
1a8c82d6db
7 changed files with 669 additions and 105 deletions
|
|
@ -37,7 +37,9 @@ func runDevOpsBook(scanner *bufio.Scanner) {
|
|||
if n, err := strconv.Atoi(input); err == nil {
|
||||
if n >= 1 && n <= len(devopsmodules.All) {
|
||||
devopsmodules.All[n-1].Run()
|
||||
waitDevOpsBack(scanner)
|
||||
if waitDevOpsBack(scanner) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
||||
|
|
@ -46,7 +48,9 @@ func runDevOpsBook(scanner *bufio.Scanner) {
|
|||
}
|
||||
if m, ok := devopsmodules.Find(input); ok {
|
||||
m.Run()
|
||||
waitDevOpsBack(scanner)
|
||||
if waitDevOpsBack(scanner) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ раздела, b — назад.%s\n",
|
||||
|
|
@ -72,28 +76,11 @@ func showDevOpsMenu() {
|
|||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||
}
|
||||
|
||||
type devopsBackSignal struct{}
|
||||
|
||||
func waitDevOpsBack(scanner *bufio.Scanner) {
|
||||
func waitDevOpsBack(scanner *bufio.Scanner) bool {
|
||||
fmt.Printf("\n %s%s[Enter — назад в меню DevOps, b — главное меню]%s ",
|
||||
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||
if scanner.Scan() {
|
||||
if IsBack(strings.TrimSpace(scanner.Text())) {
|
||||
panic(devopsBackSignal{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
orig := DevOpsBook.Run
|
||||
DevOpsBook.Run = func(scanner *bufio.Scanner) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(devopsBackSignal); !ok {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
orig(scanner)
|
||||
return IsBack(strings.TrimSpace(scanner.Text()))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// GoBook — запись справочника Go в главном меню
|
||||
var GoBook = Book{
|
||||
Key: "go",
|
||||
Aliases: []string{"g", "golang"},
|
||||
|
|
@ -32,19 +31,18 @@ func runGoBook(scanner *bufio.Scanner) {
|
|||
if IsQuit(input) {
|
||||
fmt.Printf("\n %s%sДо свидания!%s\n\n",
|
||||
utils.ColorBold, utils.ColorCyan, utils.ColorReset)
|
||||
// сигнализируем главному циклу о выходе через os.Exit
|
||||
// чтобы не усложнять — просто выходим
|
||||
return
|
||||
}
|
||||
if IsBack(input) {
|
||||
return // назад в главное меню
|
||||
return
|
||||
}
|
||||
|
||||
// По номеру
|
||||
if n, err := strconv.Atoi(input); err == nil {
|
||||
if n >= 1 && n <= len(gomodules.All) {
|
||||
gomodules.All[n-1].Run()
|
||||
waitBack(scanner)
|
||||
if goWaitBack(scanner) {
|
||||
return // b — выйти в главное меню
|
||||
}
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
||||
|
|
@ -52,10 +50,11 @@ func runGoBook(scanner *bufio.Scanner) {
|
|||
continue
|
||||
}
|
||||
|
||||
// По ключу
|
||||
if m, ok := gomodules.Find(input); ok {
|
||||
m.Run()
|
||||
waitBack(scanner)
|
||||
if goWaitBack(scanner) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +63,16 @@ func runGoBook(scanner *bufio.Scanner) {
|
|||
}
|
||||
}
|
||||
|
||||
// goWaitBack возвращает true если пользователь нажал b (выйти в главное меню)
|
||||
func goWaitBack(scanner *bufio.Scanner) bool {
|
||||
fmt.Printf("\n %s%s[Enter — назад в меню Go, b — главное меню]%s ",
|
||||
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||
if scanner.Scan() {
|
||||
return IsBack(strings.TrimSpace(scanner.Text()))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func showGoMenu() {
|
||||
utils.Header("Справочник Go")
|
||||
|
||||
|
|
@ -89,34 +98,3 @@ func showGoMenu() {
|
|||
fmt.Printf(" %s%sРаздел (номер / ключ / b — главное меню / q — выход):%s ",
|
||||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||
}
|
||||
|
||||
func waitBack(scanner *bufio.Scanner) {
|
||||
fmt.Printf("\n %s%s[Enter — назад в меню Go, b — главное меню]%s ",
|
||||
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||
if scanner.Scan() {
|
||||
if IsBack(strings.TrimSpace(scanner.Text())) {
|
||||
// вернёмся на уровень выше — используем специальный возврат
|
||||
// реализуем через панику+recover для чистоты навигации
|
||||
panic(backSignal{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// backSignal — сигнал для возврата на уровень выше из waitBack
|
||||
type backSignal struct{}
|
||||
|
||||
func init() {
|
||||
// Оборачиваем runGoBook чтобы перехватывать backSignal
|
||||
orig := GoBook.Run
|
||||
GoBook.Run = func(scanner *bufio.Scanner) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(backSignal); !ok {
|
||||
panic(r) // пробрасываем не наши паники
|
||||
}
|
||||
// backSignal — просто возвращаемся в главное меню, ничего не делаем
|
||||
}
|
||||
}()
|
||||
orig(scanner)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@ func runInfoSecBook(scanner *bufio.Scanner) {
|
|||
if n, err := strconv.Atoi(input); err == nil {
|
||||
if n >= 1 && n <= len(infosecmodules.All) {
|
||||
infosecmodules.All[n-1].Run()
|
||||
waitInfoSecBack(scanner)
|
||||
if waitInfoSecBack(scanner) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
||||
|
|
@ -46,7 +48,9 @@ func runInfoSecBook(scanner *bufio.Scanner) {
|
|||
}
|
||||
if m, ok := infosecmodules.Find(input); ok {
|
||||
m.Run()
|
||||
waitInfoSecBack(scanner)
|
||||
if waitInfoSecBack(scanner) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ раздела, b — назад.%s\n",
|
||||
|
|
@ -72,28 +76,11 @@ func showInfoSecMenu() {
|
|||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||
}
|
||||
|
||||
type infosecBackSignal struct{}
|
||||
|
||||
func waitInfoSecBack(scanner *bufio.Scanner) {
|
||||
func waitInfoSecBack(scanner *bufio.Scanner) bool {
|
||||
fmt.Printf("\n %s%s[Enter — назад в меню InfoSec, b — главное меню]%s ",
|
||||
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||
if scanner.Scan() {
|
||||
if IsBack(strings.TrimSpace(scanner.Text())) {
|
||||
panic(infosecBackSignal{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
orig := InfoSecBook.Run
|
||||
InfoSecBook.Run = func(scanner *bufio.Scanner) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(infosecBackSignal); !ok {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
orig(scanner)
|
||||
return IsBack(strings.TrimSpace(scanner.Text()))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,9 @@ func runLinuxBook(scanner *bufio.Scanner) {
|
|||
if n, err := strconv.Atoi(input); err == nil {
|
||||
if n >= 1 && n <= len(linuxmodules.All) {
|
||||
linuxmodules.All[n-1].Run()
|
||||
waitLinuxBack(scanner)
|
||||
if waitLinuxBack(scanner) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
||||
|
|
@ -53,7 +55,9 @@ func runLinuxBook(scanner *bufio.Scanner) {
|
|||
// По ключу
|
||||
if m, ok := linuxmodules.Find(input); ok {
|
||||
m.Run()
|
||||
waitLinuxBack(scanner)
|
||||
if waitLinuxBack(scanner) {
|
||||
return
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -88,28 +92,11 @@ func showLinuxMenu() {
|
|||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||
}
|
||||
|
||||
type linuxBackSignal struct{}
|
||||
|
||||
func waitLinuxBack(scanner *bufio.Scanner) {
|
||||
func waitLinuxBack(scanner *bufio.Scanner) bool {
|
||||
fmt.Printf("\n %s%s[Enter — назад в меню Linux, b — главное меню]%s ",
|
||||
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||
if scanner.Scan() {
|
||||
if IsBack(strings.TrimSpace(scanner.Text())) {
|
||||
panic(linuxBackSignal{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
orig := LinuxBook.Run
|
||||
LinuxBook.Run = func(scanner *bufio.Scanner) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(linuxBackSignal); !ok {
|
||||
panic(r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
orig(scanner)
|
||||
return IsBack(strings.TrimSpace(scanner.Text()))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
617
modules/gomodules/go_composite.go
Normal file
617
modules/gomodules/go_composite.go
Normal file
|
|
@ -0,0 +1,617 @@
|
|||
package gomodules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var CompositeModule = Module{
|
||||
Key: "composite",
|
||||
Aliases: []string{"c", "comp", "slice", "map", "struct"},
|
||||
Title: "Составные типы",
|
||||
Description: "Массивы, срезы, append, отображения, структуры, JSON и HTML",
|
||||
Run: showComposite,
|
||||
}
|
||||
|
||||
func showComposite() {
|
||||
utils.Header("Go — Составные типы")
|
||||
|
||||
// ─── Массивы ──────────────────────────────────────────────────
|
||||
utils.Section("Массивы (Arrays)")
|
||||
|
||||
utils.Println("Массив — фиксированной длины последовательность элементов одного типа.")
|
||||
utils.Println("Длина является частью типа: " +
|
||||
utils.ColorBold + utils.ColorCyan + "[3]int" + utils.ColorReset +
|
||||
" и " +
|
||||
utils.ColorBold + utils.ColorCyan + "[4]int" + utils.ColorReset +
|
||||
" — разные типы.")
|
||||
fmt.Println()
|
||||
utils.Code(`// Объявление:
|
||||
var a [3]int // [0 0 0] — нулевые значения
|
||||
var b [3]int = [3]int{1, 2, 3}
|
||||
c := [3]int{1, 2, 3}
|
||||
d := [...]int{1, 2, 3, 4, 5} // компилятор считает длину сам
|
||||
|
||||
// Инициализация с индексами:
|
||||
e := [5]string{1: "один", 3: "три"} // остальные — ""
|
||||
|
||||
// Доступ:
|
||||
fmt.Println(c[0]) // 1
|
||||
fmt.Println(len(c)) // 3
|
||||
|
||||
// Итерация:
|
||||
for i, v := range c {
|
||||
fmt.Printf("c[%d] = %d\n", i, v)
|
||||
}`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Сравнение массивов:" + utils.ColorReset)
|
||||
utils.Code(`// Массивы можно сравнивать через == и !=
|
||||
// (если элементы сравниваемы):
|
||||
a := [3]int{1, 2, 3}
|
||||
b := [3]int{1, 2, 3}
|
||||
fmt.Println(a == b) // true
|
||||
|
||||
// Передача в функцию — КОПИЯ:
|
||||
func double(arr [3]int) { arr[0] *= 2 } // оригинал не меняется
|
||||
// Чтобы изменить — передавать указатель: *[3]int`)
|
||||
|
||||
utils.Note("Массивы в Go используются редко — чаще работают со срезами.")
|
||||
|
||||
// ─── Срезы ────────────────────────────────────────────────────
|
||||
utils.Section("Срезы (Slices)")
|
||||
|
||||
utils.Println("Срез — динамический вид на массив. Три компонента:")
|
||||
utils.Code(`// Указатель на массив + длина (len) + ёмкость (cap)
|
||||
//
|
||||
// ┌─────────────────────────────────────────┐
|
||||
// │ ptr │ len │ cap │
|
||||
// └───┬───┴───────┴─────────────────────────┘
|
||||
// │
|
||||
// ▼
|
||||
// ┌───┬───┬───┬───┬───┐
|
||||
// │ 0 │ 1 │ 2 │ 3 │ 4 │ ← underlying array
|
||||
// └───┴───┴───┴───┴───┘`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Создание срезов:" + utils.ColorReset)
|
||||
utils.Code(`// Из литерала (неявно создаёт массив):
|
||||
s := []int{1, 2, 3, 4, 5}
|
||||
|
||||
// Срез нулевых значений через make(тип, len, cap):
|
||||
s := make([]int, 5) // len=5, cap=5
|
||||
s := make([]int, 3, 10) // len=3, cap=10
|
||||
|
||||
// Нулевой срез (nil slice):
|
||||
var s []int // s == nil, len=0, cap=0
|
||||
fmt.Println(s == nil) // true
|
||||
// nil срез безопасен для append и range
|
||||
|
||||
// Срез из массива:
|
||||
a := [5]int{1, 2, 3, 4, 5}
|
||||
s := a[1:4] // элементы 1,2,3 → [2 3 4]
|
||||
// len=3, cap=4 (до конца массива)`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Операции срезов:" + utils.ColorReset)
|
||||
utils.Code(`s := []int{1, 2, 3, 4, 5}
|
||||
|
||||
// Взятие среза: s[low:high] (low включён, high — нет)
|
||||
s[1:3] // [2 3] len=2
|
||||
s[:3] // [1 2 3] low=0
|
||||
s[2:] // [3 4 5] high=len
|
||||
s[:] // копия ссылки на весь срез
|
||||
|
||||
// Трёхиндексный срез: s[low:high:max] — ограничивает cap:
|
||||
s[1:3:4] // len=2, cap=3
|
||||
|
||||
// len и cap:
|
||||
fmt.Println(len(s), cap(s)) // 5 5
|
||||
|
||||
// Срезы разделяют память с оригиналом!
|
||||
a := []int{1, 2, 3, 4, 5}
|
||||
b := a[1:3] // b = [2 3]
|
||||
b[0] = 99
|
||||
fmt.Println(a) // [1 99 3 4 5] — оригинал изменён!`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Копирование срезов (copy):" + utils.ColorReset)
|
||||
utils.Code(`src := []int{1, 2, 3, 4, 5}
|
||||
dst := make([]int, 3)
|
||||
n := copy(dst, src) // копирует min(len(dst), len(src)) элементов
|
||||
fmt.Println(dst, n) // [1 2 3] 3
|
||||
|
||||
// Полная независимая копия:
|
||||
clone := make([]int, len(src))
|
||||
copy(clone, src)
|
||||
|
||||
// Или через append:
|
||||
clone := append([]int{}, src...)`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Итерация:" + utils.ColorReset)
|
||||
utils.Code(`s := []string{"a", "b", "c"}
|
||||
|
||||
for i, v := range s { // v — копия элемента
|
||||
fmt.Println(i, v)
|
||||
}
|
||||
|
||||
for i := range s { // только индексы
|
||||
s[i] = strings.ToUpper(s[i])
|
||||
}
|
||||
|
||||
for _, v := range s { // только значения
|
||||
fmt.Println(v)
|
||||
}`)
|
||||
|
||||
// ─── append ───────────────────────────────────────────────────
|
||||
utils.Section("Функция append")
|
||||
|
||||
utils.Println("append добавляет элементы в срез, возвращая новый (возможно, с новым массивом).")
|
||||
utils.Code(`// Добавить элементы:
|
||||
s := []int{1, 2, 3}
|
||||
s = append(s, 4) // [1 2 3 4]
|
||||
s = append(s, 5, 6, 7) // [1 2 3 4 5 6 7]
|
||||
|
||||
// Добавить другой срез (оператор ...):
|
||||
other := []int{8, 9}
|
||||
s = append(s, other...) // [1 2 3 4 5 6 7 8 9]
|
||||
|
||||
// Удалить элемент по индексу i:
|
||||
i := 2
|
||||
s = append(s[:i], s[i+1:]...) // порядок сохраняется
|
||||
// Быстрое удаление (порядок не важен):
|
||||
s[i] = s[len(s)-1]
|
||||
s = s[:len(s)-1]
|
||||
|
||||
// Вставить элемент по индексу i:
|
||||
s = append(s[:i+1], s[i:]...)
|
||||
s[i] = newValue
|
||||
|
||||
// Стек (stack):
|
||||
push := func(s []int, v int) []int { return append(s, v) }
|
||||
pop := func(s []int) ([]int, int) { return s[:len(s)-1], s[len(s)-1] }`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Механизм роста ёмкости:" + utils.ColorReset)
|
||||
utils.Code(`// При нехватке cap, append выделяет новый массив (~удваивает cap):
|
||||
var s []int
|
||||
for i := 0; i < 8; i++ {
|
||||
s = append(s, i)
|
||||
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
|
||||
}
|
||||
// len=1 cap=1 [0]
|
||||
// len=2 cap=2 [0 1]
|
||||
// len=3 cap=4 [0 1 2]
|
||||
// len=4 cap=4 [0 1 2 3]
|
||||
// len=5 cap=8 [0 1 2 3 4]
|
||||
// ...`)
|
||||
|
||||
utils.Note("Всегда присваивай результат append обратно: s = append(s, v)")
|
||||
utils.Note("Передавай срез с запасом через make([]T, 0, n) если знаешь итоговый размер — избежишь лишних аллокаций.")
|
||||
|
||||
// ─── Отображения ──────────────────────────────────────────────
|
||||
utils.Section("Отображения (Maps)")
|
||||
|
||||
utils.Println("Map — ссылочный тип, хэш-таблица. Порядок итерации не гарантирован.")
|
||||
fmt.Println()
|
||||
utils.Println(utils.ColorBold + "Создание:" + utils.ColorReset)
|
||||
utils.Code(`// Через литерал:
|
||||
ages := map[string]int{
|
||||
"Alice": 30,
|
||||
"Bob": 25,
|
||||
}
|
||||
|
||||
// Через make:
|
||||
ages := make(map[string]int)
|
||||
ages := make(map[string]int, 100) // с подсказкой ёмкости
|
||||
|
||||
// Нулевое значение — nil:
|
||||
var m map[string]int // m == nil
|
||||
// m["key"] = 1 // паника! нельзя писать в nil map
|
||||
// fmt.Println(m["key"]) // 0 — читать можно, не паникует`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Операции с map:" + utils.ColorReset)
|
||||
utils.Code(`ages := map[string]int{"Alice": 30, "Bob": 25}
|
||||
|
||||
// Запись:
|
||||
ages["Carol"] = 28
|
||||
ages["Alice"] = 31 // обновить
|
||||
|
||||
// Чтение:
|
||||
age := ages["Alice"] // 31
|
||||
age = ages["unknown"] // 0 — нулевое значение, НЕ ошибка
|
||||
|
||||
// Проверка наличия ключа (comma ok idiom):
|
||||
age, ok := ages["Alice"]
|
||||
if ok {
|
||||
fmt.Println("Alice:", age)
|
||||
}
|
||||
// Коротко:
|
||||
if age, ok := ages["Alice"]; ok {
|
||||
fmt.Println("Alice:", age)
|
||||
}
|
||||
|
||||
// Удаление:
|
||||
delete(ages, "Bob")
|
||||
delete(ages, "nonexistent") // безопасно, нет ошибки
|
||||
|
||||
// Длина:
|
||||
fmt.Println(len(ages)) // 2
|
||||
|
||||
// Итерация (порядок случаен!):
|
||||
for name, age := range ages {
|
||||
fmt.Printf("%s: %d\n", name, age)
|
||||
}
|
||||
|
||||
// Итерация по отсортированным ключам:
|
||||
keys := make([]string, 0, len(ages))
|
||||
for k := range ages { keys = append(keys, k) }
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys { fmt.Println(k, ages[k]) }`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Сравнение map:" + utils.ColorReset)
|
||||
utils.Code(`// Map нельзя сравнивать через == (только с nil):
|
||||
var m map[string]int
|
||||
fmt.Println(m == nil) // true
|
||||
|
||||
// Сравнение содержимого — через reflect.DeepEqual или вручную:
|
||||
import "reflect"
|
||||
fmt.Println(reflect.DeepEqual(map1, map2))
|
||||
|
||||
// Или maps.Equal из Go 1.21:
|
||||
import "maps"
|
||||
fmt.Println(maps.Equal(map1, map2))`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Типичные паттерны:" + utils.ColorReset)
|
||||
utils.Code(`// Множество (set):
|
||||
seen := make(map[string]bool)
|
||||
seen["Alice"] = true
|
||||
if seen["Alice"] { ... }
|
||||
|
||||
// Подсчёт слов:
|
||||
words := strings.Fields(text)
|
||||
count := make(map[string]int)
|
||||
for _, w := range words { count[w]++ }
|
||||
|
||||
// Группировка:
|
||||
byDept := make(map[string][]Employee)
|
||||
for _, e := range employees {
|
||||
byDept[e.Dept] = append(byDept[e.Dept], e)
|
||||
}`)
|
||||
|
||||
// ─── Структуры ────────────────────────────────────────────────
|
||||
utils.Section("Структуры (Structs)")
|
||||
|
||||
utils.Println("Struct — агрегатный тип, объединяет поля разных типов.")
|
||||
utils.Code(`// Объявление типа:
|
||||
type Point struct {
|
||||
X, Y float64
|
||||
}
|
||||
|
||||
type Employee struct {
|
||||
ID int
|
||||
Name string
|
||||
Salary float64
|
||||
Manager *Employee // рекурсивный тип — только указатель
|
||||
}`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Создание и доступ:" + utils.ColorReset)
|
||||
utils.Code(`// Структурный литерал (по именам — рекомендуется):
|
||||
p := Point{X: 1.0, Y: 2.0}
|
||||
p := Point{1.0, 2.0} // по позиции (хрупко, не для публичных типов)
|
||||
|
||||
// Нулевое значение:
|
||||
var p Point // Point{X:0, Y:0}
|
||||
|
||||
// Доступ к полям:
|
||||
fmt.Println(p.X) // 1.0
|
||||
p.Y = 5.0
|
||||
|
||||
// Указатель на структуру:
|
||||
pp := &Point{1.0, 2.0}
|
||||
pp.X = 10 // (*pp).X = 10 — автодеференс
|
||||
fmt.Println(pp.X) // 10
|
||||
|
||||
// Через new:
|
||||
pp := new(Point) // *Point, поля = нули
|
||||
pp.X = 1.0`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Сравнение структур:" + utils.ColorReset)
|
||||
utils.Code(`// Структуры сравниваемы если все поля сравниваемы:
|
||||
type Point struct{ X, Y int }
|
||||
p1 := Point{1, 2}
|
||||
p2 := Point{1, 2}
|
||||
fmt.Println(p1 == p2) // true
|
||||
|
||||
// Поля-срезы, map, func — делают структуру несравниваемой.
|
||||
// Используй reflect.DeepEqual для глубокого сравнения.
|
||||
|
||||
// Структура как ключ map (только если сравниваема):
|
||||
type Coord struct{ Lat, Lon float64 }
|
||||
visited := map[Coord]bool{}
|
||||
visited[Coord{55.75, 37.62}] = true`)
|
||||
|
||||
// ─── Встраивание и анонимные поля ────────────────────────────
|
||||
utils.Section("Встраивание структур и анонимные поля")
|
||||
|
||||
utils.Println("Встраивание (embedding) — включение типа без имени поля.")
|
||||
utils.Println("Поля и методы встроенного типа «поднимаются» во внешний тип.")
|
||||
utils.Code(`type Animal struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (a Animal) Speak() string {
|
||||
return a.Name + " говорит"
|
||||
}
|
||||
|
||||
type Dog struct {
|
||||
Animal // анонимное поле — встраивание
|
||||
Breed string
|
||||
}
|
||||
|
||||
d := Dog{
|
||||
Animal: Animal{Name: "Шарик"},
|
||||
Breed: "Лабрадор",
|
||||
}
|
||||
|
||||
// Продвинутый доступ (promotion):
|
||||
fmt.Println(d.Name) // "Шарик" (d.Animal.Name)
|
||||
fmt.Println(d.Speak()) // "Шарик говорит" (d.Animal.Speak())
|
||||
fmt.Println(d.Breed) // "Лабрадор"
|
||||
// Явный доступ:
|
||||
fmt.Println(d.Animal.Name)`)
|
||||
|
||||
utils.Code(`// Несколько встраиваний:
|
||||
type Logger struct{ Prefix string }
|
||||
func (l Logger) Log(msg string) { fmt.Println(l.Prefix, msg) }
|
||||
|
||||
type Server struct {
|
||||
Logger // анонимное поле
|
||||
net.Listener // встроен интерфейс
|
||||
Host string
|
||||
}
|
||||
|
||||
s := Server{Logger: Logger{Prefix: "[server]"}, Host: "localhost"}
|
||||
s.Log("started") // s.Logger.Log("started")
|
||||
|
||||
// Конфликт имён — нужно явно указывать путь:
|
||||
type A struct{ X int }
|
||||
type B struct{ X int }
|
||||
type C struct { A; B }
|
||||
c := C{}
|
||||
// c.X — ошибка компиляции: ambiguous
|
||||
c.A.X = 1 // явно`)
|
||||
|
||||
utils.Code(`// Встраивание указателя:
|
||||
type Cache struct {
|
||||
*sync.Mutex // анонимный указатель
|
||||
data map[string]string
|
||||
}
|
||||
|
||||
c := Cache{
|
||||
Mutex: &sync.Mutex{},
|
||||
data: make(map[string]string),
|
||||
}
|
||||
c.Lock() // c.Mutex.Lock()
|
||||
c.data["key"] = "value"
|
||||
c.Unlock()`)
|
||||
|
||||
utils.Note("Встраивание — это композиция, не наследование. Go не имеет наследования.")
|
||||
|
||||
// ─── Работа с JSON ────────────────────────────────────────────
|
||||
utils.Section("Работа с JSON")
|
||||
|
||||
utils.Println("Пакет " + utils.ColorBold + utils.ColorCyan + "encoding/json" + utils.ColorReset +
|
||||
" — маршалинг и демаршалинг JSON.")
|
||||
fmt.Println()
|
||||
utils.Println(utils.ColorBold + "Теги структур:" + utils.ColorReset)
|
||||
utils.Code(`type Person struct {
|
||||
Name string ` + "`" + `json:"name"` + "`" + `
|
||||
Age int ` + "`" + `json:"age"` + "`" + `
|
||||
Email string ` + "`" + `json:"email,omitempty"` + "`" + ` // пропустить если пусто
|
||||
Phone string ` + "`" + `json:"-"` + "`" + ` // никогда не включать
|
||||
private string // неэкспортируемые — игнорируются
|
||||
}
|
||||
|
||||
// Теги:
|
||||
// json:"name" — имя в JSON
|
||||
// json:"name,omitempty"— пропустить нулевые значения
|
||||
// json:"-" — исключить поле`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Маршалинг (структура → JSON):" + utils.ColorReset)
|
||||
utils.Code(`import "encoding/json"
|
||||
|
||||
p := Person{Name: "Alice", Age: 30, Email: "alice@example.com"}
|
||||
|
||||
// Сериализация:
|
||||
data, err := json.Marshal(p)
|
||||
if err != nil { log.Fatal(err) }
|
||||
fmt.Println(string(data))
|
||||
// {"name":"Alice","age":30,"email":"alice@example.com"}
|
||||
|
||||
// Красивый вывод с отступами:
|
||||
data, err = json.MarshalIndent(p, "", " ")
|
||||
fmt.Println(string(data))
|
||||
// {
|
||||
// "name": "Alice",
|
||||
// "age": 30,
|
||||
// "email": "alice@example.com"
|
||||
// }`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Демаршалинг (JSON → структура):" + utils.ColorReset)
|
||||
utils.Code(`jsonStr := ` + "`" + `{"name":"Bob","age":25}` + "`" + `
|
||||
|
||||
var p Person
|
||||
if err := json.Unmarshal([]byte(jsonStr), &p); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println(p.Name, p.Age) // Bob 25
|
||||
|
||||
// Потоковое чтение из http.Response:
|
||||
resp, _ := http.Get("https://api.example.com/users")
|
||||
defer resp.Body.Close()
|
||||
var users []Person
|
||||
json.NewDecoder(resp.Body).Decode(&users)
|
||||
|
||||
// Потоковая запись в http.ResponseWriter:
|
||||
json.NewEncoder(w).Encode(users)`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Произвольный JSON (interface{} / any):" + utils.ColorReset)
|
||||
utils.Code(`// Когда структура неизвестна:
|
||||
var result map[string]any
|
||||
json.Unmarshal(data, &result)
|
||||
|
||||
name := result["name"].(string) // type assertion
|
||||
age := result["age"].(float64) // числа → float64 по умолчанию
|
||||
|
||||
// Проверка типа:
|
||||
if s, ok := result["name"].(string); ok {
|
||||
fmt.Println(s)
|
||||
}
|
||||
|
||||
// json.RawMessage — отложенный демаршалинг:
|
||||
type Response struct {
|
||||
Status string ` + "`" + `json:"status"` + "`" + `
|
||||
Payload json.RawMessage ` + "`" + `json:"payload"` + "`" + `
|
||||
}
|
||||
var resp Response
|
||||
json.Unmarshal(data, &resp)
|
||||
var user Person
|
||||
json.Unmarshal(resp.Payload, &user)`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Кастомные маршалеры:" + utils.ColorReset)
|
||||
utils.Code(`// Реализовать json.Marshaler / json.Unmarshaler:
|
||||
type Duration time.Duration
|
||||
|
||||
func (d Duration) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(time.Duration(d).String())
|
||||
}
|
||||
|
||||
func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err != nil { return err }
|
||||
dur, err := time.ParseDuration(s)
|
||||
*d = Duration(dur)
|
||||
return err
|
||||
}`)
|
||||
|
||||
// ─── Работа с HTML ────────────────────────────────────────────
|
||||
utils.Section("Работа с HTML")
|
||||
|
||||
utils.Println("Пакет " + utils.ColorBold + utils.ColorCyan + "html/template" + utils.ColorReset +
|
||||
" — безопасные HTML-шаблоны с автоматическим экранированием.")
|
||||
fmt.Println()
|
||||
utils.Code(`import "html/template"
|
||||
|
||||
// Шаблон:
|
||||
const tmpl = ` + "`" + `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Привет, {{.Name}}!</h1>
|
||||
<ul>
|
||||
{{range .Items}}
|
||||
<li>{{.}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
{{if .Admin}}
|
||||
<p>Вы администратор</p>
|
||||
{{end}}
|
||||
</body>
|
||||
</html>` + "`" + `
|
||||
|
||||
type Data struct {
|
||||
Name string
|
||||
Items []string
|
||||
Admin bool
|
||||
}
|
||||
|
||||
// Парсинг и выполнение:
|
||||
t := template.Must(template.New("page").Parse(tmpl))
|
||||
|
||||
data := Data{
|
||||
Name: "Alice",
|
||||
Items: []string{"Go", "Docker", "Kubernetes"},
|
||||
Admin: true,
|
||||
}
|
||||
|
||||
// В os.Stdout:
|
||||
t.Execute(os.Stdout, data)
|
||||
|
||||
// В http.ResponseWriter:
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
t.Execute(w, data)
|
||||
}`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Экранирование и безопасность:" + utils.ColorReset)
|
||||
utils.Code(`// html/template автоматически экранирует пользовательский ввод:
|
||||
// <script>alert(1)</script> → <script>alert(1)</script>
|
||||
|
||||
// Если нужно вставить безопасный HTML — использовать template.HTML:
|
||||
type Page struct {
|
||||
Title string
|
||||
Content template.HTML // не экранируется
|
||||
}
|
||||
|
||||
// Никогда не делай:
|
||||
// template.HTML(userInput) — XSS!
|
||||
|
||||
// text/template — без экранирования (для не-HTML):
|
||||
import "text/template"
|
||||
// Используй для генерации кода, конфигов, emails в plain text`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Шаблонные функции и Actions:" + utils.ColorReset)
|
||||
utils.Code(`// Функции в шаблоне:
|
||||
funcMap := template.FuncMap{
|
||||
"upper": strings.ToUpper,
|
||||
"add": func(a, b int) int { return a + b },
|
||||
}
|
||||
t := template.New("t").Funcs(funcMap).Parse(` + "`" + `
|
||||
{{.Name | upper}}
|
||||
{{add 2 3}}
|
||||
` + "`" + `)
|
||||
|
||||
// Actions (действия):
|
||||
{{/* комментарий */}}
|
||||
{{.Field}} // вывести поле
|
||||
{{.Method arg}} // вызов метода
|
||||
{{if .Cond}} ... {{end}}
|
||||
{{if .Cond}} ... {{else}} ... {{end}}
|
||||
{{range .Slice}} {{.}} {{end}}
|
||||
{{range .Map}} {{.}} {{end}}
|
||||
{{with .Val}} {{.}} {{end}} // установить . = Val если не ноль
|
||||
{{template "name" .}} // вложенный шаблон
|
||||
|
||||
// Шаблоны из файлов:
|
||||
t := template.Must(template.ParseFiles("index.html", "base.html"))
|
||||
t := template.Must(template.ParseGlob("templates/*.html"))`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Парсинг HTML (html пакет):" + utils.ColorReset)
|
||||
utils.Code(`import "html"
|
||||
|
||||
// Экранирование и разэкранирование HTML-сущностей:
|
||||
escaped := html.EscapeString("<script>alert('xss')</script>")
|
||||
// <script>alert('xss')</script>
|
||||
|
||||
unescaped := html.UnescapeString("<b>жирный</b>")
|
||||
// <b>жирный</b>
|
||||
|
||||
// Для разбора HTML-дерева — golang.org/x/net/html:
|
||||
import "golang.org/x/net/html"
|
||||
|
||||
resp, _ := http.Get("https://example.com")
|
||||
doc, _ := html.Parse(resp.Body)
|
||||
|
||||
// Рекурсивный обход:
|
||||
var walk func(*html.Node)
|
||||
walk = func(n *html.Node) {
|
||||
if n.Type == html.ElementNode && n.Data == "a" {
|
||||
for _, attr := range n.Attr {
|
||||
if attr.Key == "href" {
|
||||
fmt.Println(attr.Val)
|
||||
}
|
||||
}
|
||||
}
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
walk(c)
|
||||
}
|
||||
}
|
||||
walk(doc)`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ type Module struct {
|
|||
var All = []Module{
|
||||
BasicsModule,
|
||||
StructuresModule,
|
||||
CompositeModule,
|
||||
// сюда будут добавляться новые модули
|
||||
}
|
||||
|
||||
|
|
@ -109,6 +110,13 @@ func Keywords(key string) []string {
|
|||
"компиляция", "кросс-компиляция", "GOOS", "GOARCH", "golangci-lint",
|
||||
"бенчмарк", "benchmark", "покрытие", "coverage", "go env", "GOPATH",
|
||||
},
|
||||
"composite": {
|
||||
"массивы", "arrays", "срезы", "slices", "append", "make", "copy",
|
||||
"отображения", "map", "maps", "структуры", "structs", "встраивание",
|
||||
"embedding", "анонимные поля", "JSON", "html/template", "шаблоны",
|
||||
"маршалинг", "marshal", "unmarshal", "json.Encoder", "json.Decoder",
|
||||
"omitempty", "range", "len", "cap", "delete", "composite types",
|
||||
},
|
||||
"structs": {
|
||||
"переменные", "variables", "объявления", "declarations", "типы", "types",
|
||||
"указатели", "pointers", "new", "область видимости", "scope",
|
||||
|
|
|
|||
BIN
rubric
BIN
rubric
Binary file not shown.
Loading…
Add table
Reference in a new issue