Нияз Хадимуллин | Ментор по GO – Telegram
Нияз Хадимуллин | Ментор по GO
1.19K subscribers
130 photos
1 video
35 links
Авторский канал ментора Нияза про Go, базы данных и разработку

Если хочешь записаться на моё менторство и начать получать офферы, не стесняйся писать мне https://mentor-niyaz.ru
Download Telegram
📦 Карты в Go: создание и работа с nil картами

Что такое карты?

Карты (maps) — это структуры данных, которые хранят пары ключ-значение. В Go карты могут быть nil, что требует осторожности при работе с ними.

💡 Создание и использование карт:
// Создание пустой карты
m := make(map[string]int)

// Добавление элементов
m["key1"] = 1
m["key2"] = 2

// Проверка наличия ключа
value, exists := m["key1"]
if exists {
fmt.Println("key1:", value)
}

// Nil карта
var nilMap map[string]int
// nilMap["key"] = 1 // Паника: присвоение в nil map

// Пример использования
func main() {
fmt.Println(m) // map[key1:1 key2:2]
}


⚠️ Преимущества:

- Быстрый доступ к данным по ключу.
- Удобство работы с парами ключ-значение.

Ограничения:

- Nil карты нельзя использовать для записи.
- Необходимость проверки наличия ключа перед чтением
🔥36👍304
📦 Буферизованное чтение/запись в Go

Что такое буферизация?

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

💡 Пример использования bufio:
import (
"bufio"
"fmt"
"os"
)

func main() {
// Буферизованная запись
file, _ := os.Create("output.txt")
writer := bufio.NewWriter(file)
writer.WriteString("Hello, World!\n")
writer.Flush() // Не забываем сбросить буфер

// Буферизованное чтение
file, _ = os.Open("output.txt")
reader := bufio.NewReader(file)
line, _ := reader.ReadString('\n')
fmt.Println(line) // Hello, World!
}


⚠️ Преимущества:

- Уменьшение количества системных вызовов.
- Повышение производительности при работе с большими данными.

Ограничения:

- Требует ручного сброса буфера (Flush).
- Может быть избыточным для маленьких данных.
🔥4330👍20
📦 Каскадное восстановление после паники в Go

Что такое паника и восстановление?

Паника (panic) — это механизм, который останавливает выполнение программы. Восстановление (recover) позволяет перехватить панику и продолжить выполнение.

💡 Реализация каскадного восстановления:
func safeFunction() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Восстановлено:", r)
}
}()
panic("Произошла паника")
}

func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Восстановление на верхнем уровне:", r)
}
}()
safeFunction()
fmt.Println("Программа завершена")
}


⚠️ Преимущества:

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

Ограничения:

- Нельзя использовать для обычной обработки ошибок.
- Может скрывать реальные проблемы в коде.
👍5550🔥39
📦 Механизм defer в Go: отложенное выполнение

Что такое defer?

defer — это ключевое слово, которое откладывает выполнение функции до завершения текущей функции. Это полезно для освобождения ресурсов.

💡 Пример использования defer:
func readFile(filename string) {
file, err := os.Open(filename)
if err != nil {
fmt.Println("Ошибка:", err)
return
}
defer file.Close() // Закрытие файла при завершении функции

scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}

func main() {
readFile("example.txt")
}


⚠️ Преимущества:

- Гарантированное выполнение кода при завершении функции.
- Удобство для работы с ресурсами (файлы, соединения).

Ограничения:

- Нельзя использовать для контроля порядка выполнения в сложных сценариях.
- Может привести к утечкам памяти, если забыть закрыть ресурсы
👍52🔥3225
📦 Замыкания в Go: функции с состоянием

Что такое замыкание?

Замыкание — это функция, которая захватывает переменные из окружающего контекста. Это позволяет создавать функции с состоянием.

💡 Пример замыкания:
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}

func main() {
c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3
}


⚠️ Преимущества:

- Удобство для создания функций с состоянием.
- Поддержка функционального стиля программирования.

Ограничения:

- Риск утечек памяти, если замыкание захватывает большие объекты.
- Может усложнить чтение кода
👍57🔥3733
🛠️ Основные команды Git: добавление, коммит, пуш, слияние, сброс и создание веток

Как работать с Git?

Git — это система контроля версий, которая позволяет отслеживать изменения в коде. Вот основные команды:

💡 Основные команды Git:
- Добавление изменений:
   git add <file>  # Добавляет файл в индекс
git add . # Добавляет все изменения в индекс

- Коммит изменений:
   git commit -m "Сообщение коммита"  # Создает коммит с изменениями

- Пуш изменений в репозиторий:
   git push origin <branch>  # Отправляет изменения в удаленный репозиторий

- Получение изменений из ветки:
   git merge <branch>   # Сливает изменения из указанной ветки в текущую
git rebase <branch> # Перебазирует текущую ветку на указанную

- Берем изменения из другой ветки:
   git cherry-pick <commit_hash>  # Применяет конкретный коммит из другой ветки

- Удаляем текущие изменения:
   git reset --hard  # Сбрасывает все изменения до последнего коммита

- Создание/переключение ветки:
   git checkout -b <new_branch>  # Создает новую ветку и переключается на нее
git checkout <branch> # Переключается на существующую ветку

- Флаг `--no-verify`:
   git commit --no-verify  # Пропускает pre-commit хуки


⚠️ Когда использовать эти команды?
- Для управления изменениями в коде и совместной работы.
- Для интеграции изменений из разных веток.

🎯 Git — это мощный инструмент для управления версиями кода.
40🔥38👍31
🛠️ TLS vs SSL: что это и зачем они нужны?

Что такое TLS и SSL?

TLS (Transport Layer Security) и SSL (Secure Sockets Layer) — это протоколы для шифрования данных, передаваемых по сети. Они обеспечивают безопасность связи между клиентом и сервером.

💡 Разница между TLS и SSL:
- SSL: Устаревший протокол, который был заменен на TLS.
- TLS: Современный протокол, который обеспечивает более высокий уровень безопасности.

⚠️ Когда использовать TLS/SSL?
- Для защиты данных, передаваемых по сети (например, пароли, платежные данные).
- Для обеспечения конфиденциальности и целостности данных.

🎯 TLS и SSL — это ключевые технологии для обеспечения безопасности в интернете.
👍4136🔥34
🛠️ Модули в Golang: что это и зачем они нужны?

Что такое модули в Go?

Модули в Go — это способ управления зависимостями в проекте. Они позволяют указывать версии зависимостей и обеспечивают воспроизводимость сборки.

💡 Как использовать модули в Go?
- Инициализация модуля:
   go mod init <module_name>  # Создает новый модуль

- Добавление зависимостей:
   go get <package>  # Добавляет зависимость в модуль

- Обновление зависимостей:
   go mod tidy  # Удаляет неиспользуемые зависимости


⚠️ Когда использовать модули?
- Для управления зависимостями в проекте.
- Для обеспечения воспроизводимости сборки.

🎯 Модули в Go — это мощный инструмент для управления зависимостями.
🔥70👍5141
🛠️ Операции в map в Golang и их сложности

Что такое map в Go?

Map — это структура данных, которая хранит пары ключ-значение. В Go map реализована как хэш-таблица.

💡 Операции в map и их сложности:
- Вставка: map[key] = value — O(1) в среднем.
- Поиск: value, ok := map[key] — O(1) в среднем.
- Удаление: delete(map, key) — O(1) в среднем.

⚠️ Пример:  m := make(map[string]int)
m["key"] = 42 // Вставка
value, ok := m["key"] // Поиск
delete(m, "key") // Удаление


В map нет потокобезопасности: Требуется синхронизация при использовании в многопоточных приложениях.

🎯 Map в Go — это эффективная структура данных для хранения пар ключ-значение.
🔥35👍2721
🛠️ Вложенные map в Golang

Что такое вложенные map?

Вложенные map — это map, где значениями являются другие map. Это полезно для хранения сложных структур данных.

💡 Пример вложенной map:  m := make(map[string]map[string]int)
m["outer"] = make(map[string]int)
m["outer"]["inner"] = 42


⚠️ Когда использовать вложенные map?
- Для хранения иерархических данных.
- Когда требуется гибкость в структуре данных.

🎯 Вложенные map — это мощный инструмент для хранения сложных данных в Go.
👍3331🔥30
🛠️ KISS (Keep It Simple, Stupid)

Что такое KISS?

KISS — это принцип проектирования, который гласит, что системы должны быть максимально простыми. Чем проще решение, тем легче его поддерживать и развивать.

💡 Как применять KISS?
- Избегайте излишней сложности в коде.
- Разбивайте задачи на более мелкие и простые части.
- Используйте понятные и прямолинейные решения.

⚠️ Пример:
Вместо сложного алгоритма с множеством условий используйте простой и понятный код:

func sum(a, b int) int {
return a + b
}


🎯 KISS — это ключ к созданию поддерживаемого и надежного кода.
👍3431🔥30
🛠️ YAGNI (You Aren’t Gonna Need It)

Что такое YAGNI?

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

💡 Как применять YAGNI?
- Не добавляйте код "на будущее".
- Реализуйте только то, что требуется прямо сейчас.
- Рефакторите код, когда появляются новые требования.

⚠️ Пример:
Не создавайте сложную систему логирования, если достаточно простого вывода в консоль:

fmt.Println("Error:", err)


🎯 YAGNI — это принцип, который помогает сосредоточиться на важном и избежать избыточности.
👍60🔥3123
🛠️ GRASP (General Responsibility Assignment Software Patterns)

Что такое GRASP?

GRASP — это набор принципов для распределения ответственностей между классами и объектами в объектно-ориентированном проектировании. Основные принципы включают:
- Информационный эксперт (Information Expert).
- Создатель (Creator).
- Контроллер (Controller).
- Низкая связанность (Low Coupling).
- Высокая связность (High Cohesion).

💡 Как применять GRASP?
- Информационный эксперт: Назначайте ответственность классу, который владеет необходимой информацией.
- Низкая связанность: Минимизируйте зависимости между классами.
- Высокая связность: Классы должны выполнять одну четкую задачу.

⚠️ Пример:
Если класс Order содержит информацию о заказе, он должен отвечать за расчет стоимости:

type Order struct {
Items []Item
}

func (o Order) CalculateTotal() float64 {
total := 0.0
for _, item := range o.Items {
total += item.Price
}
return total
}


🎯 GRASP — это набор принципов для создания качественного объектно-ориентированного дизайна.
🔥45👍3527
🛠️ Преобразование типов: строка в []byte и обратно в Go

Как преобразовывать строки и байты в Go?

В Go строки и срезы байт (`[]byte`) тесно связаны, так как строка — это неизменяемая последовательность байт. Преобразование между ними выполняется просто.

⚠️ Пример использования:
package main

import "fmt"

func main() {
s := "Go"
b := []byte(s) // Строка -> []byte
fmt.Println(b) // [71 111]

s2 := string(b) // []byte -> строка
fmt.Println(s2) // Go
}


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

🎯 Преобразование строк и байтов — это базовая операция в Go, которая часто используется в реальных приложениях.
🔥3822👍20
🛠️ Операторы `break` и `continue` в Go

Что такое `break` и `continue`?

- `break`: Прерывает выполнение цикла (for, switch, select).
- `continue`: Пропускает текущую итерацию цикла и переходит к следующей.

💡 Как использовать `break` и `continue`?
- `break`:
  for i := 0; i < 10; i++ {
if i == 5 {
break // Выход из цикла при i == 5
}
fmt.Println(i)
}

- `continue`:
  for i := 0; i < 10; i++ {
if i%2 == 0 {
continue // Пропуск четных чисел
}
fmt.Println(i)
}


⚠️ Пример с вложенными циклами:
outer:
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
if i == 1 && j == 1 {
break outer // Выход из обоих циклов
}
fmt.Println(i, j)
}
}


Почему это важно?
- Управление выполнением: break и continue позволяют гибко управлять логикой циклов.
- Упрощение кода: Помогают избежать сложных условий и вложенных конструкций.

🎯 `break` и `continue` — это ключевые операторы для управления циклами в Go.
👍45🔥4328
🔐 X.509: Цифровые сертификаты

Что такое X.509?

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

💡 Что содержит X.509 сертификат?
- Открытый ключ владельца
- Информация о владельце (DN - Distinguished Name)
- Срок действия
- Цифровая подпись удостоверяющего центра (CA)

Version: 3
Serial Number: 1234567890
Subject: CN=example.com, O=Example Inc, C=US
Issuer: CN=DigiCert Global Root CA
Valid From: 2023-01-01
Valid To: 2024-01-01
Public Key: RSA (2048 bits)


Почему это важно?
- Безопасность: Обеспечивает доверенную связь в интернете
- Аутентификация: Подтверждает подлинность веб-сайтов

🎯 X.509 — фундамент современной безопасности в интернете
👍30🔥2821
🔐 Web of Trust: Децентрализованное доверие

Что такое Web of Trust?

Web of Trust (WoT) — это концепция децентрализованного доверия, где пользователи сами подтверждают подлинность ключей друг друга. Популярна в PGP/GPG.

💡 Как работает WoT?
- Каждый пользователь создаёт пару ключей
- Пользователи подписывают ключи тех, кого знают лично
- Создаётся сеть доверительных отношений

⚠️ Пример подписи ключа в GPG:  # Подписать ключ
gpg --sign-key user@example.com

# Экспортировать подпись
gpg --export --armor user@example.com


Почему это важно?
- Децентрализация: Нет единой точки отказа
- Гибкость: Каждый определяет уровень доверия сам

🎯 WoT — альтернатива централизованным CA в определённых сценариях
🔥59👍5225
🔐 SSH: Безопасный доступ

Что такое SSH?

SSH (Secure Shell) — протокол для безопасного удалённого доступа к системам. Обеспечивает шифрование и аутентификацию.

💡 Ключевые особенности:
- Асимметричное шифрование для аутентификации
- Симметричное шифрование для передачи данных
- Проверка целостности сообщений

⚠️ Пример использования:  # Генерация ключевой пары
ssh-keygen -t ed25519 -C "user@example.com"

# Безопасное подключение
ssh user@server.com -p 22


Почему это важно?
- Безопасность: Защита от перехвата данных
- Универсальность: Стандарт для удалённого доступа

🎯 SSH — незаменимый инструмент для безопасного удалённого управления
👍6247🔥23
🔐 HMAC: Проверка целостности

Что такое HMAC?

HMAC (Hash-based Message Authentication Code) — это механизм проверки целостности данных, использующий криптографическую хеш-функцию и секретный ключ.

💡 Как работает HMAC?
- Комбинирует секретный ключ с данными
- Применяет хеш-функцию (SHA-256, SHA-3 и др.)
- Создаёт уникальный код аутентификации

func generateHMAC(message, key []byte) []byte {
mac := hmac.New(sha256.New, key)
mac.Write(message)
return mac.Sum(nil)
}


Почему это важно?
- Целостность: Гарантирует неизменность данных
- Аутентификация: Подтверждает источник данных

🎯 HMAC — стандарт для проверки целостности в криптографии
52🔥31👍23
🔐 Salt: Защита паролей

Что такое Salt?

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

💡 Правила использования Salt:
- Уникальный для каждого пароля
- Достаточной длины (минимум 16 байт)
- Генерируется криптографически безопасно


⚠️ Пример на Go: import (
"crypto/rand"
"encoding/base64"
"golang.org/x/crypto/argon2"
)

func hashPassword(password string) (hash, salt []byte) {
// Генерируем соль
salt = make([]byte, 16)
rand.Read(salt)

// Хешируем пароль с солью
hash = argon2.IDKey(
[]byte(password),
salt,
1, // количество итераций
64*1024, // используемая память
4, // количество потоков
32, // длина выходного хеша
)

return hash, salt
}


Почему это важно?
- Уникальность: Одинаковые пароли дают разные хеши
- Безопасность: Защита от предварительно вычисленных атак

🎯 Salt — обязательный элемент современного хеширования паролей
👍7150🔥27
🏗️ Внедрение зависимостей (Dependency Injection)

Что такое Dependency Injection?

Dependency Injection (DI) — паттерн проектирования, который позволяет сделать код более модульным, тестируемым и гибким за счёт управления зависимостями извне.

💡 Ключевые концепции:
- Инверсия управления (IoC)
- Внедрение зависимостей через конструктор
- Слабая связанность компонентов
- Простота тестирования
- Возможность подмены реализаций
// Интерфейс репозитория
type UserRepository interface {
FindByID(id int) (*User, error)
Save(user *User) error
}

// Сервис, зависящий от репозитория
type UserService struct {
repo UserRepository
}

// Конструктор с внедрением зависимости
func NewUserService(repo UserRepository) *UserService {
return &UserService{repo: repo}
}

// Реальная имплементация репозитория
type PostgresUserRepository struct {
db *sql.DB
}

func (r *PostgresUserRepository) FindByID(id int) (*User, error) {
// Реализация поиска
}

// В тестах можно использовать мок
type MockUserRepository struct {
users map[int]*User
}

func (m *MockUserRepository) FindByID(id int) (*User, error) {
return m.users[id], nil
}

Почему это важно?
- Модульность: Легкая замена компонентов
- Тестируемость: Простое создание mock-объектов
- Гибкость: Независимость компонентов
- Расширяемость: Простое добавление новой функциональности

🎯 DI — ключ к созданию гибких и поддерживаемых систем
🔥94👍8837