💬Какова цель функции init() в Go?
📌Функция
📌Несколько ключевых моментов:
1. Автоматический вызов: функция
2. Использование: функции
3. Несколько функций
4. В случае зависимостей между пакетами, функции
👉 Подробнее
📌Функция
init()
в Go вызывается автоматически при инициализации пакета. В Go нет конструкторов в классическом понимании, как в некоторых других ЯП, но функция init()
предлагает возможность выполнять необходимую начальную настройку. 📌Несколько ключевых моментов:
1. Автоматический вызов: функция
init()
вызывается автоматически перед вызовом main()
и не требует явного вызова.2. Использование: функции
init()
можно использовать для инициализации глобальных переменных, проверки или установки конфигурации, установки соединений с базами данных и других целей.3. Несколько функций
init()
: в одном пакете можно иметь несколько функций init()
. Они будут вызваны в том порядке, в котором объявлены в файле.4. В случае зависимостей между пакетами, функции
init()
из импортированных пакетов выполняются перед функцией init()
из основного пакета.Please open Telegram to view this post
VIEW IN TELEGRAM
Digitalocean
Функция init в Go | DigitalOcean
В Go заданнная функция init() выделяет элемент кода, который запускатся до любой другой части вашего пакета. Этот код запускается сразу же после импорта паке…
👍5
💬Как переобъявить переменные с помощью коротких объявлений?
📌В одной области видимости нельзя переобъявлять переменные, но это можно делать в объявлении нескольких переменных (multi-variable declarations), среди которых хотя бы одна — новая. Переобъявляемые переменные должны располагаться в том же блоке, иначе получится затенённая переменная.
📌Неправильно:
📌Правильно:
📌В одной области видимости нельзя переобъявлять переменные, но это можно делать в объявлении нескольких переменных (multi-variable declarations), среди которых хотя бы одна — новая. Переобъявляемые переменные должны располагаться в том же блоке, иначе получится затенённая переменная.
📌Неправильно:
func main() {
one := 0
one := 1
// ошибка компиляции
}
📌Правильно:
func main() {
one := 0
one, two := 1,2
one,two = two,one
}
👍10
💬В чем разница между пакетами и модулями Go?
📌В Go, пакет — это коллекция исходных файлов
📌Файлы для одного пакета должны находиться в одной директории. Полное имя для пакета строится из имени модуля и пути к директории с файлами.
◆ Например, в
◆ Например, в дереве исходников нашей библиотеки есть файлы в директоории
📌В Go, пакет — это коллекция исходных файлов
.go
в одной директории и с одинаковой директивой package
, в то время как модуль — это дерево пакетов. Имя модуля задаётся в go.mod
.📌Файлы для одного пакета должны находиться в одной директории. Полное имя для пакета строится из имени модуля и пути к директории с файлами.
◆ Например, в
go.mod
указано module example.org/mylib
, тогда все пакеты из модуля example.org/mylib
должны быть в дочерних директориях относительно go.mod
, и путь к директории определяет имя пакета.◆ Например, в дереве исходников нашей библиотеки есть файлы в директоории
./cmd/root
. Тогда эти файлы должны быть либо с директивой package root
, либо package root_test
. И полное имя пакета для этих файлов будет либо example.org/mylib/cmd/root
, либо example.org/mylib/cmd/root_test
(
тесты для пакета example.org/mylib/cmd/root)
.👍12
💬Предположим, что мы хотим выполнить код Go в какой-то момент в будущем или повторно через определенный интервал. Что необходимо использовать в таком случае?
📌Встроенные функции
🔹Таймеры (первый пример):
◆ Таймеры представляют собой способ ожидания определенного времени перед выполнением действия.
◆ Создание таймера выполняется с помощью функции
◆ В примере таймер имеет канал
◆ Также возможно остановить таймер перед его активацией с помощью метода
🔹Тикеры (второй пример):
◆ Тикеры используются для выполнения действий через регулярные промежутки времени.
◆ Создание тикера также выполняется с помощью функции в пакете
◆ Аналогично таймерам, тикеры имеют канал
◆ Тикеры можно остановить, используя метод
📌Встроенные функции
timer
и ticker
упрощают обе задачи, при этом таймеры предназначены для кейсов, когда мы хотим сделать что-то один раз в будущем, а тикеры — для кейсов, когда мы хотим сделать что-то повторно через определенные промежутки времени.🔹Таймеры (первый пример):
◆ Таймеры представляют собой способ ожидания определенного времени перед выполнением действия.
◆ Создание таймера выполняется с помощью функции
time.NewTimer()
; в нее передаем длительность времени, которую необходимо ожидать.◆ В примере таймер имеет канал
C
, в который будет отправлено значение после истечения заданного времени.◆ Также возможно остановить таймер перед его активацией с помощью метода
Stop()
.🔹Тикеры (второй пример):
◆ Тикеры используются для выполнения действий через регулярные промежутки времени.
◆ Создание тикера также выполняется с помощью функции в пакете
time
, в данном случае time.NewTicker()
, с указанием интервала между «тиками».◆ Аналогично таймерам, тикеры имеют канал
C
, в который отправляется значение на каждом «тике».◆ Тикеры можно остановить, используя метод
Stop()
, что предотвратит дальнейшее отправление значений.👍9🥱4
💬Как обрабатывать сигналы Unix в Go?
🔸Обработка сигналов UNIX в Go обычно выполняется с использованием пакета
📌Простой пример:
1. Импортируем пакет
2. Создаем канал, который будет использоваться для получения сигналов:
3. Используем функцию
4. Используем
// обработка сигнала
📌Программа ожидает сигналы
👉 Подробнее
🔸Обработка сигналов UNIX в Go обычно выполняется с использованием пакета
os/signal
. 📌Простой пример:
1. Импортируем пакет
os/signal
и syscall
:import (
"os"
"os/signal"
"syscall"
)
2. Создаем канал, который будет использоваться для получения сигналов:
signals := make(chan os.Signal, 1)
3. Используем функцию
signal.Notify
, чтобы указать, какие сигналы мы хотим обрабатывать:signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
4. Используем
select
в горутине или в основном потоке программы для ожидания сигналов и реагирования на них:select {
case sig := <-signals:
// обработка сигнала
fmt.Printf("received signal %v\n", sig)
}
📌Программа ожидает сигналы
SIGINT
и SIGTERM
, и когда она их получит, выведет полученный сигнал. Это базовый пример того, как можно обрабатывать сигналы в Go. 👉 Подробнее
👍13
💬 Что возвращает функция len() в Go, если ей передана строка в кодировке UTF-8?
🔸Функция
🔸Если строка закодирована в UTF-8, каждый символ может занимать от 1 до 4 байтов.
🔸Таким образом, если в строке UTF-8 присутствуют многобайтовые символы, функция
🔸Результат:
Byte length: 6
Rune count: 2
🔸Функция
len()
в Go возвращает количество байтов в строке, а не количество рун (символов Unicode).🔸Если строка закодирована в UTF-8, каждый символ может занимать от 1 до 4 байтов.
🔸Таким образом, если в строке UTF-8 присутствуют многобайтовые символы, функция
len()
вернет значение, большее, чем количество символов в строке.
import (
"fmt"
"unicode/utf8"
)
func main() {
s := "世界"
fmt.Println("Byte length:", len(s))
fmt.Println("Rune count:", utf8.RuneCountInString(s))
}
🔸Результат:
Byte length: 6
Rune count: 2
👍6
💬Какие риски возникают при использовании нескольких тегов полей в одной структуре?
📌Основная проблема при использовании нескольких тегов полей в одной структуре заключается в том, что это может привести к тесной связанности между различными слоями нашего приложения.
📌Простой пример:
🔸В примере у структуры Po
🔸При использовании подобных тегов слой HTTP-ответа (веб-сервер) и слой хранения (MongoDB) становятся тесно связанными. Если мы хотим изменить ti
📌Основная проблема при использовании нескольких тегов полей в одной структуре заключается в том, что это может привести к тесной связанности между различными слоями нашего приложения.
📌Простой пример:
type Post struct {
Title string `json:"title" bson:"title"`
SubTitle string `json:"subtitle" bson:"subtitle"`
}
🔸В примере у структуры Po
st д
ля каждого поля есть два тега поля: json и
bson.
Эти теги могут использоваться в разных целях, например, для отправки HTTP-ответов (используя json)
и обработки демаршализации MongoDB (используя bson).
🔸При использовании подобных тегов слой HTTP-ответа (веб-сервер) и слой хранения (MongoDB) становятся тесно связанными. Если мы хотим изменить ti
tle н
а, например, shortTitle,
нам нужно будет обновить и HTTP-ответ (что также может повлиять на клиентов, обрабатывающих ответ), и хранение в MongoDB.👍1
🧑💻 Статьи для IT: как объяснять и распространять значимые идеи
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
💬Где полезен встроенный метод recover в Go?
Встроенный метод
// Плохо
func main() {
defer recover()
panicCode()
}
// Лучше
📌Кроме того:
🔸Обработка ошибок: в ситуациях, когда паника является возможной и ожидаемой, например, при работе с внешними ресурсами или библиотеками, которые могут вызывать панику, re
🔸Неотложное восстановление: в демонизированных приложениях или долго работающих процессах, где стабильность имеет критическое значение, re
🔸Откат транзакций: в операциях, которые должны быть атомарными, таких как обновления базы данных, re
🔸Логирование и отладка: re
Встроенный метод
recover
действительно полезен в отложенных функциях, но не рекомендуется вызывать его напрямую с помощью ключевого слова defer
.// Плохо
func main() {
defer recover()
panicCode()
}
// Лучше
func handlePanic() {
if panicInfo := recover(); panicInfo != nil {
fmt.Println(panicInfo)
}
}
func main() {
defer handlePanic()
panicCode()
}
📌Кроме того:
🔸Обработка ошибок: в ситуациях, когда паника является возможной и ожидаемой, например, при работе с внешними ресурсами или библиотеками, которые могут вызывать панику, re
cover м
ожет быть использован для возврата ошибки вместо завершения программы.🔸Неотложное восстановление: в демонизированных приложениях или долго работающих процессах, где стабильность имеет критическое значение, re
cover м
ожет использоваться для обеспечения того, чтобы временная ошибка не привела к полному сбою системы.🔸Откат транзакций: в операциях, которые должны быть атомарными, таких как обновления базы данных, re
cover м
ожет использоваться для обнаружения паники и выполнения отката транзакции, чтобы поддерживать целостность данных.🔸Логирование и отладка: re
cover м
ожет использоваться для перехвата паники, логирования диагностической информации и затем повторного вызова паники, чтобы стандартный процесс обработки ошибок мог продолжить работу.❤8👍1
💬Для чего в Go предназначена директива "//go:embed"
📌Начиная с Go 1.16, директива
📌Во время выполнения, эти ресурсы становятся доступны через переменную, к которой применяется директива. Нам не нужно заботиться о механизмах пути к файлу или открытии и чтении файлов во время выполнения, что упрощает распространение программ, требующих доступа к внешним файлам.
Простой пример:
Все файлы в директории static встраиваются в бинарный файл. Директива //
Этот функционал особенно полезен, когда мы хотим создать самодостаточное приложение, которое не зависит от внешних файлов во время выполнения.
👉 Подробнее
?
📌Начиная с Go 1.16, директива
//go:embed
представляет собой специальный комментарий, который используется для встраивания файлов и директорий непосредственно в скомпилированный бинарный файл Go.📌Во время выполнения, эти ресурсы становятся доступны через переменную, к которой применяется директива. Нам не нужно заботиться о механизмах пути к файлу или открытии и чтении файлов во время выполнения, что упрощает распространение программ, требующих доступа к внешним файлам.
Простой пример:
package main
import (
"embed"
"io/fs"
"net/http"
)
//go:embed static/*
var staticFiles embed.FS
func main() {
// Используем встроенные файлы напрямую
http.Handle("/", http.FileServer(http.FS(staticFiles)))
http.ListenAndServe(":8080", nil)
}
Все файлы в директории static встраиваются в бинарный файл. Директива //
go:embed д
олжна быть расположена непосредственно перед объявлением переменной без пустых строк между комментарием и объявлением. Это позволяет использовать staticFiles к
ак файловую систему внутри Go-кода.Этот функционал особенно полезен, когда мы хотим создать самодостаточное приложение, которое не зависит от внешних файлов во время выполнения.
👉 Подробнее
👍13🔥2
💬Что в себя включает реализация кастомного обработчика логов с log/slog?
🔸Пакет стандартной библиотеки
🔸Пакет
📌Для создания кастомного обработчика существует 4 метода:
1️⃣ Enabled: представляет собой оптимизацию, позволяющую избежать ненужной работы. Метод вывода
2️⃣ Handle: этому методу передается файл
3️⃣ WithAttrs. Одной из оптимизаций производительности
Обработчик может сохранить атрибуты для последующего использования методом
4️⃣ WithGroup:
👉 Подробнее
🔸Пакет стандартной библиотеки
log/slog
состоит из двух частей: внешней и внутренней. Внешняя часть реализована типом Logger
, собирает структурированную информацию логов, и передает их внутренней части, реализации интерфейса Handler
. 🔸Пакет
slog
поставляется с двумя встроенными обработчиками, которых обычно должно быть достаточно. Но нам может потребоваться написать кастомный обработчик. 📌Для создания кастомного обработчика существует 4 метода:
1️⃣ Enabled: представляет собой оптимизацию, позволяющую избежать ненужной работы. Метод вывода
Logger
будет вызываться Enabled
перед обработкой любого из своих аргументов, чтобы проверить, следует ли продолжать работу.Enabled(context.Context, Level) bool
2️⃣ Handle: этому методу передается файл
Record
, содержащий всю информацию, которая должна быть зарегистрирована для одного вызова метода вывода Logger.Handle(context.Context, Record) error
3️⃣ WithAttrs. Одной из оптимизаций производительности
slog
является поддержка атрибутов предварительного форматирования. Метод Logger.With преобразует пары ключ-значение в Attrs
, а затем вызывает Handler.WithAttrs
. Обработчик может сохранить атрибуты для последующего использования методом
Handle
или может воспользоваться возможностью отформатировать атрибуты сейчас, один раз, а не делать это повторно при каждом вызове Handle
.WithAttrs(attrs []Attr) Handler
4️⃣ WithGroup:
Logger.WithGroup
вызывает Handler.WithGroup
напрямую, с тем же аргументом, именем группы. Обработчик должен запомнить имя, чтобы использовать его для определения всех последующих атрибутов.WithGroup(name string) Handler
👉 Подробнее
❤2👍1
💬В чем разница между длиной и емкостью среза в Go?
📌Внутри срез содержит указатель на резервный массив, а также длину и емкость👇
◾Длина среза указывает на количество элементов, которые в данный момент содержатся в срезе. Это число элементов, доступных для непосредственного использования и итерации.
◾Емкость среза отражает общее количество элементов, которые срез может содержать, не требуя дополнительного выделения памяти.
◽️Если при добавлении новых элементов в срез его длина превышает емкость, система выделит новый, больший участок памяти для этого среза, и старый участок памяти будет освобожден.
◽️Важно отметить, что длина среза не может превышать его емкость. В Go есть встроенные функции
◽️Когда мы создаем срез, мы можем указать как его длину, так и емкость. Если указать только длину, емкость будет установлена равной длине. Мы можем создавать срезы с начальной емкостью больше длины, чтобы оптимизировать производительность при последующем добавлении элементов, минимизируя количество операций выделения памяти.
📌Простой пример:
◽️В данном случае создается массив из шести элементов, но поскольку длина была установлена равной 3, Go инициализирует только первые три элемента. Кроме того, поскольку срез является типом
👉О распространенных ошибках при работе со срезами читайте здесь.
📌Внутри срез содержит указатель на резервный массив, а также длину и емкость👇
◾Длина среза указывает на количество элементов, которые в данный момент содержатся в срезе. Это число элементов, доступных для непосредственного использования и итерации.
◾Емкость среза отражает общее количество элементов, которые срез может содержать, не требуя дополнительного выделения памяти.
◽️Если при добавлении новых элементов в срез его длина превышает емкость, система выделит новый, больший участок памяти для этого среза, и старый участок памяти будет освобожден.
◽️Важно отметить, что длина среза не может превышать его емкость. В Go есть встроенные функции
len
и cap
, которые позволяют получить длину и емкость среза. ◽️Когда мы создаем срез, мы можем указать как его длину, так и емкость. Если указать только длину, емкость будет установлена равной длине. Мы можем создавать срезы с начальной емкостью больше длины, чтобы оптимизировать производительность при последующем добавлении элементов, минимизируя количество операций выделения памяти.
📌Простой пример:
s := make([]int, 3, 6)
fmt.Println(s) // [0 0 0]
◽️В данном случае создается массив из шести элементов, но поскольку длина была установлена равной 3, Go инициализирует только первые три элемента. Кроме того, поскольку срез является типом
[]int
, первые три элемента инициализируются нулевым значением int
: 0.👉О распространенных ошибках при работе со срезами читайте здесь.
100go.co
Not understanding slice length and capacity (#20) - 100 Go Mistakes and How to Avoid Them
None
👍7🔥2🥱2
💬Что из себя представляет Принцип инверсии зависимостей (Dependency Inversion Principle, DIP) в Go?
DIP — это один из пяти принципов SOLID, который направлен на уменьшение зависимостей в коде. Суть в том, что:
1️⃣Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба типа модулей должны зависеть от абстракций.
2️⃣Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
3️⃣Другими словами, зависимости должны быть инвертированы вниз, в сторону абстракций.
📌В контексте Go, это может быть реализовано с использованием интерфейсов. Например:
В этом примере
Это улучшает гибкость и расширяемость кода, а также облегчает тестирование, т. к. можно использовать моки или фейки для интерфейса
DIP — это один из пяти принципов SOLID, который направлен на уменьшение зависимостей в коде. Суть в том, что:
1️⃣Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба типа модулей должны зависеть от абстракций.
2️⃣Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
3️⃣Другими словами, зависимости должны быть инвертированы вниз, в сторону абстракций.
📌В контексте Go, это может быть реализовано с использованием интерфейсов. Например:
// Зависимость (абстракция)
type Database interface {
Connect() string
}
// Конкретная реализация
type MySQL struct {}
func (db MySQL) Connect() string {
return "Connected to MySQL"
}
// Модуль высокого уровня
type Application struct {
db Database
}
func NewApplication(database Database) Application {
return Application{db: database}
}
func (app Application) Start() {
fmt.Println(app.db.Connect())
}
func main() {
mysql := MySQL{}
app := NewApplication(mysql)
app.Start()
}
В этом примере
Application
является модулем высокого уровня, и он зависит от абстракции Database
, а не от конкретной реализации MySQL
. Такой подход позволяет легко заменить MySQL
на другую реализацию Database
, не изменяя при этом код Application
. Это улучшает гибкость и расширяемость кода, а также облегчает тестирование, т. к. можно использовать моки или фейки для интерфейса
Database
при тестировании Application
.👍20
💬Что из себя представляет graceful shutdown (правильное завершение работы) в контексте Go?
🔹Graceful shutdown — это процедура безопасного завершения работы приложения, где важно корректно обработать все текущие операции перед остановкой.
🔹Это особенно важно в высоконагруженных и распределенных системах, где внезапное прерывание может привести к потере данных или другим проблемам.
📌В Go, graceful shutdown обычно достигается следующим образом:
1️⃣Перехват сигналов ОС: настраиваем обработчики для сигналов ОС, таких как
2️⃣Завершение текущих задач: после получения сигнала о завершении приложение пытается корректно завершить текущие операции. Это может включать в себя завершение обработки текущих запросов, закрытие открытых соединений и освобождение ресурсов.
3️⃣Отказ от новых запросов: во время graceful shutdown приложение перестает принимать новые запросы, чтобы сосредоточиться на корректном завершении текущих операций.
4️⃣Синхронизация горутин: если приложение использует горутины, необходимо убедиться, что они все завершили свою работу перед остановкой программы. Это часто достигается с помощью каналов и синхронизации.
5️⃣Освобождение ресурсов: перед полным завершением работы приложение должно корректно освободить все используемые ресурсы, такие как файловые дескрипторы, соединения с базами данных и т. д.
🔹Примером реализации graceful shutdown в Go может быть использование пакета
🔹Graceful shutdown — это процедура безопасного завершения работы приложения, где важно корректно обработать все текущие операции перед остановкой.
🔹Это особенно важно в высоконагруженных и распределенных системах, где внезапное прерывание может привести к потере данных или другим проблемам.
📌В Go, graceful shutdown обычно достигается следующим образом:
1️⃣Перехват сигналов ОС: настраиваем обработчики для сигналов ОС, таких как
SIGINT
(сигнал прерывания, обычно инициируемый нажатием Ctrl+C) и SIGTERM
(сигнал запроса на завершение, отправляемый, например, системными утилитами для остановки процесса). Это позволяет приложению реагировать на попытки его завершения.2️⃣Завершение текущих задач: после получения сигнала о завершении приложение пытается корректно завершить текущие операции. Это может включать в себя завершение обработки текущих запросов, закрытие открытых соединений и освобождение ресурсов.
3️⃣Отказ от новых запросов: во время graceful shutdown приложение перестает принимать новые запросы, чтобы сосредоточиться на корректном завершении текущих операций.
4️⃣Синхронизация горутин: если приложение использует горутины, необходимо убедиться, что они все завершили свою работу перед остановкой программы. Это часто достигается с помощью каналов и синхронизации.
5️⃣Освобождение ресурсов: перед полным завершением работы приложение должно корректно освободить все используемые ресурсы, такие как файловые дескрипторы, соединения с базами данных и т. д.
🔹Примером реализации graceful shutdown в Go может быть использование пакета
context
для контроля времени жизни горутин и использование пакета net/http
для управления HTTP-сервером таким образом, чтобы он корректно завершал обработку текущих запросов перед остановкой.👍16
💬Представьте, что вам необходимо внедрить информацию о версии и другие метаданные в ваше Go-приложение во время сборки, не изменяя исходный код. Как это реализовать?
📌Мы можем использовать флаг
📌Простые юзкейсы:
◆ Установка значения переменной: мы можем установить значение переменной во время компиляции. Например,
◆ Уменьшение размера бинарного файла: использование
👉 Подробнее
📌Мы можем использовать флаг
-ldflags
, который позволяет управлять поведением компоновщика при сборке Go-программ. Он позволяет определять опции сборки на этапе компиляции. 📌Простые юзкейсы:
◆ Установка значения переменной: мы можем установить значение переменной во время компиляции. Например,
go build -ldflags "-X main.version=1.0.0"
устанавливает переменную version в пакете main в значение 1.0.0.◆ Уменьшение размера бинарного файла: использование
go build -ldflags "-w -s"
позволяет уменьшить размер исполняемого файла, отключая отладочную информацию и символы таблицы.👉 Подробнее
Digitalocean
Использование ldflags для установки информации о версиях в приложениях Go | DigitalOcean
При развертывании приложений в производственной среде сборка двоичных файлов с информацией о версии и другими метаданными помогает улучшить процессы монитори…
👍10
💬В чем разница между nil и пустым срезом в Go?
◾️Чтобы избежать распространенных ошибок, важно понимать разницу между nil и пустым срезом. Оба представляют собой срезы нулевой длины и нулевой емкости, но только nil срез не требует выделения памяти.
◾️Nil срез равен nil, в то время как пустой срез имеет нулевую длину. Nil срез является пустым, но пустой срез не обязательно является nil.
📌Мы можем инициализировать срез в зависимости от контекста, используя:
☑️
☑️
☑️
◾️
◾️Чтобы избежать распространенных ошибок, важно понимать разницу между nil и пустым срезом. Оба представляют собой срезы нулевой длины и нулевой емкости, но только nil срез не требует выделения памяти.
◾️Nil срез равен nil, в то время как пустой срез имеет нулевую длину. Nil срез является пустым, но пустой срез не обязательно является nil.
📌Мы можем инициализировать срез в зависимости от контекста, используя:
☑️
var s []string
, если мы не уверены в окончательной длине и срез может быть пустым☑️
[]string(nil)
как синтаксический сахар для создания nil и пустого среза☑️
make([]string, length)
, если будущая длина известна◾️
[]string{}
следует избегать, если мы инициализируем срез без элементов.👍5
💬 Если ключ или значение типа map имеют размер более 128 байт, каким образом Go их будет хранить?
📌Если ключ или значение мапы превышает 128 байт, Go не сохранит его непосредственно в бакете мапы. Вместо этого Go сохраняет указатель на ключ или значение.
📌Хоть все происходит под капотом, это может значительно повлиять на производительность и управление памятью.
👉 Читайте подробнее об утечках памяти при работе с мапами
📌Если ключ или значение мапы превышает 128 байт, Go не сохранит его непосредственно в бакете мапы. Вместо этого Go сохраняет указатель на ключ или значение.
📌Хоть все происходит под капотом, это может значительно повлиять на производительность и управление памятью.
👉 Читайте подробнее об утечках памяти при работе с мапами
100go.co
Maps and memory leaks (#28) - 100 Go Mistakes and How to Avoid Them
None
👍9🔥1
💬Как использовать операторы == и != для эффективного сравнения значений в Go?
📌Мы можем использовать эти операторы с операндами, которые сравнимы:
• Логические: равны ли два логических значения.
• Числовые (int, float, complex): равны ли два числовых значения.
• Строки: равны ли две строки.
• Каналы: созданы ли два канала одним вызовом make или оба равны nil.
• Интерфейсы: имеют ли два интерфейса идентичные динамические типы и равные динамические значения или оба равны nil.
• Указатели: указывают ли два указателя на одно и то же значение в памяти или оба равны nil.
• Структуры и массивы: состоят ли они из аналогичных типов.
📌Также мы можем использовать операторы <=,
📌Например, в Go мы можем использовать
📌Важно помнить, что в стандартной библиотеке есть некоторые существующие методы сравнения, такие как
📌Мы можем использовать эти операторы с операндами, которые сравнимы:
• Логические: равны ли два логических значения.
• Числовые (int, float, complex): равны ли два числовых значения.
• Строки: равны ли две строки.
• Каналы: созданы ли два канала одним вызовом make или оба равны nil.
• Интерфейсы: имеют ли два интерфейса идентичные динамические типы и равные динамические значения или оба равны nil.
• Указатели: указывают ли два указателя на одно и то же значение в памяти или оба равны nil.
• Структуры и массивы: состоят ли они из аналогичных типов.
📌Также мы можем использовать операторы <=,
>=
, <
и >
с числовыми типами для сравнения значений и со строками для сравнения их лексического порядка. Если операнды несравнимы, мы должны использовать другие варианты, такие как рефлексия. 📌Например, в Go мы можем использовать
reflect.DeepEqual
. Эта функция сообщает, равны ли два элемента, рекурсивно обходя два значения. Элементы, которые она принимает, это базовые типы, массивы, структуры, срезы, мапы, указатели, интерфейсы и функции. Однако основной недостаток — это производительность.📌Важно помнить, что в стандартной библиотеке есть некоторые существующие методы сравнения, такие как
bytes.Compare
, slices.Compare
и другие.❤5👍2
💬Как эффективно инициализировать тип map в Go?
🔸Мапа представляет собой неупорядоченную коллекцию пар ключ-значение, в которой все ключи различны. Под капотом мапа основана на структуре данных хеш-таблицы, которая в свою очередь представляет собой массив бакетов, где каждый бакет — это указатель на массив пар ключ-значение.
🔸Если мы заранее знаем количество элементов, которые будет содержать мапа, эффективнее будет создать ее, указав начальный размер. Это позволяет избежать потенциального расширения мапы, что довольно сложно с точки зрения вычислений, поскольку требует перераспределения достаточного пространства памяти и перебалансировки всех элементов.
🔸Мапа представляет собой неупорядоченную коллекцию пар ключ-значение, в которой все ключи различны. Под капотом мапа основана на структуре данных хеш-таблицы, которая в свою очередь представляет собой массив бакетов, где каждый бакет — это указатель на массив пар ключ-значение.
🔸Если мы заранее знаем количество элементов, которые будет содержать мапа, эффективнее будет создать ее, указав начальный размер. Это позволяет избежать потенциального расширения мапы, что довольно сложно с точки зрения вычислений, поскольку требует перераспределения достаточного пространства памяти и перебалансировки всех элементов.
❤7👍3🥱1