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, err := strconv.Atoi(input); err == nil {
|
||||||
if n >= 1 && n <= len(devopsmodules.All) {
|
if n >= 1 && n <= len(devopsmodules.All) {
|
||||||
devopsmodules.All[n-1].Run()
|
devopsmodules.All[n-1].Run()
|
||||||
waitDevOpsBack(scanner)
|
if waitDevOpsBack(scanner) {
|
||||||
|
return
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
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 {
|
if m, ok := devopsmodules.Find(input); ok {
|
||||||
m.Run()
|
m.Run()
|
||||||
waitDevOpsBack(scanner)
|
if waitDevOpsBack(scanner) {
|
||||||
|
return
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ раздела, b — назад.%s\n",
|
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ раздела, b — назад.%s\n",
|
||||||
|
|
@ -72,28 +76,11 @@ func showDevOpsMenu() {
|
||||||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||||
}
|
}
|
||||||
|
|
||||||
type devopsBackSignal struct{}
|
func waitDevOpsBack(scanner *bufio.Scanner) bool {
|
||||||
|
|
||||||
func waitDevOpsBack(scanner *bufio.Scanner) {
|
|
||||||
fmt.Printf("\n %s%s[Enter — назад в меню DevOps, b — главное меню]%s ",
|
fmt.Printf("\n %s%s[Enter — назад в меню DevOps, b — главное меню]%s ",
|
||||||
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||||
if scanner.Scan() {
|
if scanner.Scan() {
|
||||||
if IsBack(strings.TrimSpace(scanner.Text())) {
|
return 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 false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GoBook — запись справочника Go в главном меню
|
|
||||||
var GoBook = Book{
|
var GoBook = Book{
|
||||||
Key: "go",
|
Key: "go",
|
||||||
Aliases: []string{"g", "golang"},
|
Aliases: []string{"g", "golang"},
|
||||||
|
|
@ -32,19 +31,18 @@ func runGoBook(scanner *bufio.Scanner) {
|
||||||
if IsQuit(input) {
|
if IsQuit(input) {
|
||||||
fmt.Printf("\n %s%sДо свидания!%s\n\n",
|
fmt.Printf("\n %s%sДо свидания!%s\n\n",
|
||||||
utils.ColorBold, utils.ColorCyan, utils.ColorReset)
|
utils.ColorBold, utils.ColorCyan, utils.ColorReset)
|
||||||
// сигнализируем главному циклу о выходе через os.Exit
|
|
||||||
// чтобы не усложнять — просто выходим
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if IsBack(input) {
|
if IsBack(input) {
|
||||||
return // назад в главное меню
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// По номеру
|
|
||||||
if n, err := strconv.Atoi(input); err == nil {
|
if n, err := strconv.Atoi(input); err == nil {
|
||||||
if n >= 1 && n <= len(gomodules.All) {
|
if n >= 1 && n <= len(gomodules.All) {
|
||||||
gomodules.All[n-1].Run()
|
gomodules.All[n-1].Run()
|
||||||
waitBack(scanner)
|
if goWaitBack(scanner) {
|
||||||
|
return // b — выйти в главное меню
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
||||||
|
|
@ -52,10 +50,11 @@ func runGoBook(scanner *bufio.Scanner) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// По ключу
|
|
||||||
if m, ok := gomodules.Find(input); ok {
|
if m, ok := gomodules.Find(input); ok {
|
||||||
m.Run()
|
m.Run()
|
||||||
waitBack(scanner)
|
if goWaitBack(scanner) {
|
||||||
|
return
|
||||||
|
}
|
||||||
continue
|
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() {
|
func showGoMenu() {
|
||||||
utils.Header("Справочник Go")
|
utils.Header("Справочник Go")
|
||||||
|
|
||||||
|
|
@ -89,34 +98,3 @@ func showGoMenu() {
|
||||||
fmt.Printf(" %s%sРаздел (номер / ключ / b — главное меню / q — выход):%s ",
|
fmt.Printf(" %s%sРаздел (номер / ключ / b — главное меню / q — выход):%s ",
|
||||||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
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, err := strconv.Atoi(input); err == nil {
|
||||||
if n >= 1 && n <= len(infosecmodules.All) {
|
if n >= 1 && n <= len(infosecmodules.All) {
|
||||||
infosecmodules.All[n-1].Run()
|
infosecmodules.All[n-1].Run()
|
||||||
waitInfoSecBack(scanner)
|
if waitInfoSecBack(scanner) {
|
||||||
|
return
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
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 {
|
if m, ok := infosecmodules.Find(input); ok {
|
||||||
m.Run()
|
m.Run()
|
||||||
waitInfoSecBack(scanner)
|
if waitInfoSecBack(scanner) {
|
||||||
|
return
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ раздела, b — назад.%s\n",
|
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ раздела, b — назад.%s\n",
|
||||||
|
|
@ -72,28 +76,11 @@ func showInfoSecMenu() {
|
||||||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||||
}
|
}
|
||||||
|
|
||||||
type infosecBackSignal struct{}
|
func waitInfoSecBack(scanner *bufio.Scanner) bool {
|
||||||
|
|
||||||
func waitInfoSecBack(scanner *bufio.Scanner) {
|
|
||||||
fmt.Printf("\n %s%s[Enter — назад в меню InfoSec, b — главное меню]%s ",
|
fmt.Printf("\n %s%s[Enter — назад в меню InfoSec, b — главное меню]%s ",
|
||||||
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||||
if scanner.Scan() {
|
if scanner.Scan() {
|
||||||
if IsBack(strings.TrimSpace(scanner.Text())) {
|
return 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 false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ func runLinuxBook(scanner *bufio.Scanner) {
|
||||||
if n, err := strconv.Atoi(input); err == nil {
|
if n, err := strconv.Atoi(input); err == nil {
|
||||||
if n >= 1 && n <= len(linuxmodules.All) {
|
if n >= 1 && n <= len(linuxmodules.All) {
|
||||||
linuxmodules.All[n-1].Run()
|
linuxmodules.All[n-1].Run()
|
||||||
waitLinuxBack(scanner)
|
if waitLinuxBack(scanner) {
|
||||||
|
return
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
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 {
|
if m, ok := linuxmodules.Find(input); ok {
|
||||||
m.Run()
|
m.Run()
|
||||||
waitLinuxBack(scanner)
|
if waitLinuxBack(scanner) {
|
||||||
|
return
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,28 +92,11 @@ func showLinuxMenu() {
|
||||||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||||
}
|
}
|
||||||
|
|
||||||
type linuxBackSignal struct{}
|
func waitLinuxBack(scanner *bufio.Scanner) bool {
|
||||||
|
|
||||||
func waitLinuxBack(scanner *bufio.Scanner) {
|
|
||||||
fmt.Printf("\n %s%s[Enter — назад в меню Linux, b — главное меню]%s ",
|
fmt.Printf("\n %s%s[Enter — назад в меню Linux, b — главное меню]%s ",
|
||||||
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||||
if scanner.Scan() {
|
if scanner.Scan() {
|
||||||
if IsBack(strings.TrimSpace(scanner.Text())) {
|
return 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 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{
|
var All = []Module{
|
||||||
BasicsModule,
|
BasicsModule,
|
||||||
StructuresModule,
|
StructuresModule,
|
||||||
|
CompositeModule,
|
||||||
// сюда будут добавляться новые модули
|
// сюда будут добавляться новые модули
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,6 +110,13 @@ func Keywords(key string) []string {
|
||||||
"компиляция", "кросс-компиляция", "GOOS", "GOARCH", "golangci-lint",
|
"компиляция", "кросс-компиляция", "GOOS", "GOARCH", "golangci-lint",
|
||||||
"бенчмарк", "benchmark", "покрытие", "coverage", "go env", "GOPATH",
|
"бенчмарк", "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": {
|
"structs": {
|
||||||
"переменные", "variables", "объявления", "declarations", "типы", "types",
|
"переменные", "variables", "объявления", "declarations", "типы", "types",
|
||||||
"указатели", "pointers", "new", "область видимости", "scope",
|
"указатели", "pointers", "new", "область видимости", "scope",
|
||||||
|
|
|
||||||
BIN
rubric
BIN
rubric
Binary file not shown.
Loading…
Add table
Reference in a new issue