Golang | Вопросы собесов
4.33K subscribers
27 photos
694 links
Download Telegram
🤔 Возможен ли JOIN со вложенными запросами?

Да, в SQL возможен JOIN с вложенными запросами. Это позволяет объединять результаты подзапросов с другими таблицами, что полезно, когда нужно предварительно отфильтровать или агрегировать данные перед объединением.

🚩Как это работает?

JOIN можно использовать не только с реальными таблицами, но и с результатами подзапросов, если они представляют собой временные таблицы.

🚩Пример: JOIN с подзапросом

Допустим, у нас есть две таблицы:
- orders (id, user_id, total)
- users (id, name)
Мы хотим получить всех пользователей и их заказы, но только те заказы, где сумма больше 100
SELECT u.name, sub.total
FROM users u
JOIN (
SELECT user_id, total
FROM orders
WHERE total > 100
) AS sub ON u.id = sub.user_id;


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 В чем разница между императивным и декларативным?

Императивный подход описывает, как выполнить задачу, а декларативный — что нужно сделать, оставляя детали выполнения системе.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 В чем разница процесса и потока в рамках операционной системы?

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

🚩Процесс

Это экземпляр выполняющейся программы. Процесс имеет собственное изолированное адресное пространство памяти, что означает, что память, которую использует один процесс, не может быть напрямую доступна другому процессу. Каждый процесс предоставляет ресурсы, необходимые для выполнения программы, включая память, дескрипторы файлов, и переменные среды. Процессы изолированы друг от друга, что повышает безопасность и устойчивость системы в целом, поскольку сбой в одном процессе обычно не влияет на другие процессы. Однако эта изоляция требует больших затрат ресурсов при переключении между процессами и при их создании.

🚩Поток (Thread)

Это более легковесная единица выполнения, которая существует внутри процесса. Все потоки внутри одного процесса делят одно и то же адресное пространство памяти и системные ресурсы, такие как файловые дескрипторы. Это позволяет потокам более эффективно общаться друг с другом, поскольку они могут обмениваться данными без использования специальных механизмов межпроцессного взаимодействия. Потоки идеально подходят для выполнения задач, которые могут быть эффективно распараллелены, поскольку они позволяют программе выполнять множество операций одновременно. Однако, поскольку потоки делят память, разработчику необходимо тщательно управлять доступом к общим данным, чтобы избежать условий гонки и других проблем синхронизации.

🚩Основные различия

🟠Изоляция
Процессы изолированы друг от друга, в то время как потоки делят состояние и ресурсы внутри одного процесса.

🟠Память
Каждый процесс имеет собственное адресное пространство, в то время как все потоки внутри процесса делят его адресное пространство.

🟠Создание и управление
Создание нового процесса более ресурсоемко, чем создание потока внутри существующего процесса.

🟠Взаимодействие
Взаимодействие между процессами требует использования межпроцессного взаимодействия (IPC), такого как сокеты, разделяемая память, очереди сообщений и т. д. Потоки внутри процесса могут общаться друг с другом напрямую через общую память.

🟠Надежность и безопасность
Ошибка в одном процессе обычно не влияет на другие процессы, но ошибка в одном потоке может привести к сбою всего процесса.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое шардинг?

Шардинг — это метод горизонтального разделения базы данных, при котором данные распределяются между несколькими серверами (шардами) для улучшения производительности и масштабируемости. Каждый шард хранит уникальный поднабор данных, позволяя распределить нагрузку и снизить время отклика при больших объемах данных. Шардинг часто используется в распределенных системах для обеспечения более эффективного управления данными.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Почему нельзя брать ссылку на значение, хранящееся по ключу в map?

Нельзя напрямую взять ссылку на значение, хранящееся по ключу в карте (map), из-за особенностей реализации карт и управления памятью. Рассмотрим подробнее, почему это так.

🚩Причины, почему нельзя брать ссылку на значение в карте

🟠Внутреннее устройство карты (map)
Карты реализованы на основе хеш-таблиц. Внутреннее устройство карты предполагает, что значения могут перемещаться в памяти при выполнении операций, таких как добавление или удаление элементов. Хеш-таблица может перераспределять (реорганизовывать) свои внутренние структуры для оптимизации доступа к элементам. Это может происходить, например, когда карта увеличивается в размере.

🟠Потенциальная недействительность ссылок
Если бы была возможность брать ссылки на значения, хранящиеся в карте, то при любой операции изменения карты (добавление, удаление элементов) ссылки могли бы становиться недействительными. Это привело бы к потенциально небезопасному поведению программы, так как указатель (ссылка) мог бы указывать на уже несуществующую или перемещенную область памяти.

🚩Демонстрация проблемы

Здесь попытка взять ссылку на значение из карты могла бы привести к проблемам
package main

import "fmt"

func main() {
m := map[string]int{"a": 1, "b": 2}

// Нельзя делать так:
// p := &m["a"]

// Вместо этого можно работать с копией значения
value := m["a"]
p := &value

fmt.Println("Value:", *p) // 1

// Изменение карты
m["c"] = 3

// Ссылка на значение в карте могла бы стать недействительной
// fmt.Println("Value after map change:", *p)
}


🚩Правильные способы работы со значениями карты

Для работы с ними лучше использовать копии значений. Вот несколько способов, как это можно сделать:

🟠Работа с копией значения
Получить значение из карты и сохранить его в переменную.
value := m["a"]   


🟠Изменение значения в карте
Если необходимо изменить значение в карте, его нужно сначала извлечь, изменить, а затем снова записать в карту.
value := m["a"]
value = value + 10
m["a"] = value


🟠Использование указателей в качестве значений
В некоторых случаях можно использовать указатели в качестве значений карты, чтобы можно было изменять значения через указатели.
   package main

import "fmt"

func main() {
m := map[string]*int{"a": new(int), "b": new(int)}
*m["a"] = 1
*m["b"] = 2

// Теперь можно брать указатели на значения
p := m["a"]
fmt.Println("Value:", *p) // 1

// Изменение значения через указатель
*p = 42
fmt.Println("Updated Value:", *m["a"]) // 42
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как строки работают в Golang внутри?

В Go строки представляют собой неизменяемые последовательности байтов, закодированных в UTF-8. Под капотом строка — это структура, которая хранит указатель на массив байтов и длину строки. Строки неизменяемы, что означает, что при любых операциях с ними создается новая строка. Это обеспечивает безопасность и позволяет легко работать с текстом, но при этом делает операции изменения относительно дорогими.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие бывают типы в Go?

В Go существует несколько основных типов, которые можно разделить на следующие категории: базовые типы, агрегированные типы, ссылочные типы и интерфейсы. Рассмотрим каждую категорию более подробно.

🚩Базовые типы

🟠Числовые типы
Целые числа:
int (размер зависит от платформы):int8, int16, int32, int64 ; uint (беззнаковый, размер зависит от платформы) ; uint8 (также известен как byte), uint16, uint32, uint64
Числа с плавающей запятой:float32, float64
Комплексные числа:complex64, complex128

🟠Булевый тип
bool (значения true и false)

🟠Символьные типы
byte (аналог uint8)
rune (аналог int32, используется для представления символов Unicode)

🟠Строки
string (последовательность байт, используемая для хранения текста)

🚩Агрегированные типы

🟠Массивы
Фиксированный размер: [n]T (например, [5]int для массива из пяти целых чисел)

🟠Срезы
Переменный размер: []T (например, []int для среза целых чисел)

🟠Структуры
Набор полей: struct (например, type Person struct { Name string; Age int })

🚩Ссылочные типы

🟠Указатели
Хранит адрес переменной: *T (например, *int для указателя на целое число)

🟠Карты (map)
Хранит пары ключ-значение: map[K]V (например, map[string]int для карты, где ключи - строки, а значения - целые числа)

🟠Функции
Определяет тип функции: func(параметры) возвращаемые_типы (например, func(int) int для функции, принимающей целое число и возвращающей целое число)

🟠Интерфейсы
Определяет методы, которые должны быть реализованы: interface{} (например, type Stringer interface { String() string })

package main

import "fmt"

// Определение структуры
type Person struct {
Name string
Age int
}

// Функция, использующая интерфейс
func (p Person) String() string {
return fmt.Sprintf("%s (%d years old)", p.Name, p.Age)
}

func main() {
// Пример использования базовых типов
var age int = 30
var name string = "Alice"
var isStudent bool = false

// Пример использования агрегированных типов
var scores [3]int = [3]int{90, 85, 88}
var people []Person = []Person{
{Name: "Bob", Age: 25},
{Name: "Charlie", Age: 35},
}

// Пример использования ссылочных типов
var p *Person = &Person{Name: "Dave", Age: 40}
var ageMap map[string]int = map[string]int{"Alice": 30, "Bob": 25}

// Вывод данных
fmt.Println("Name:", name)
fmt.Println("Age:", age)
fmt.Println("Is student:", isStudent)
fmt.Println("Scores:", scores)
fmt.Println("People:", people)
fmt.Println("Pointer to Person:", p)
fmt.Println("Age map:", ageMap)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 В чём отличие UInt от Int?

- Int — знаковое целое число, может быть как положительным, так и отрицательным.
- UInt — беззнаковое целое число, только 0 и положительные значения.
UInt полезен, если ты точно знаешь, что число не может быть отрицательным (например, размер массива). Однако арифметические операции между Int и UInt требуют явного преобразования.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие преимущества есть у Go?

Go (или Golang) обладает рядом преимуществ, которые делают его популярным.

🟠Простота и лаконичность
Был разработан с упором на простоту синтаксиса и лаконичность. Это делает язык легким для изучения и чтения кода. Минимализм языка позволяет разработчикам сосредоточиться на решении задач, а не на сложностях синтаксиса.

🟠Высокая производительность
Компилируется в машинный код, что обеспечивает высокую производительность выполнения программ. Производительность Go сопоставима с производительностью программ, написанных на C или C++, благодаря низкоуровневой оптимизации компилятора.

🟠Параллелизм и конкурентность
Одним из ключевых преимуществ является встроенная поддержка параллелизма и конкурентности. С помощью горутин и каналов разработчики могут легко создавать многопоточные приложения.

package main

import (
"fmt"
"time"
)

func say(s string) {
for i := 0; i < 3; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}

func main() {
go say("world")
say("hello")
}


🟠Сильная система типов и безопасность памяти
Имеет строгую систему типов, которая помогает предотвращать ошибки на этапе компиляции. В языке отсутствуют неявные преобразования типов, что снижает вероятность ошибок. Также Go управляет памятью с помощью встроенного сборщика мусора (garbage collector), что предотвращает утечки памяти.

🟠Встроенная поддержка
Стандартных инструментов
Поставляется с богатым набором встроенных инструментов для разработки, таких как:
go fmt для автоматического форматирования кода.
go test для запуска тестов.
go build и go run для сборки и выполнения программ.
go doc для генерации документации.

🟠Кроссплатформенность
Поддерживает компиляцию кода для различных платформ и операционных систем. Это делает его удобным для разработки кроссплатформенных приложений.

🟠Стандартная библиотека
Обширна и покрывает многие аспекты разработки, такие как работа с сетью, работа с файлами, веб-разработка и многое другое. Это позволяет разработчикам быстро начинать работу, не тратя время на поиск и интеграцию сторонних библиотек.

🟠Сообщество и поддержка
Имеет большое и активное сообщество, а также поддерживается компанией Google. Это гарантирует наличие множества ресурсов для обучения и решения возникающих вопросов, а также активное развитие и улучшение языка.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие бывают примитивы синхронизации?

Примитивы — это средства, предотвращающие конфликты между потоками:
- Mutex — взаимное исключение.
- Semaphore — ограничение количества одновременных доступов.
- Spinlock — цикл ожидания без сна.
- RWLock (чтение-запись) — позволяет множественное чтение, но только одну запись.
- Atomic операции — безопасные базовые действия без блокировок.
- Condition variables — ожидание события от другого потока.
- Channel / Queue — для безопасного обмена данными (особенно в Go).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое SLI?

Это конкретная метрика, используемая для измерения качества обслуживания сервиса. SLI показывает, насколько хорошо сервис соответствует определенным критериям, установленным в SLO (Service Level Objective).

🚩Основные компоненты

🟠Доступность (Availability)
Процент времени, в течение которого сервис доступен для пользователей. Пример: "99.9% времени в течение месяца."
🟠Производительность (Performance)
Метрики, такие как время отклика или пропускная способность. Пример: "Среднее время отклика 200 миллисекунд."
🟠Время восстановления (MTTR - Mean Time to Recovery)
Среднее время, необходимое для восстановления сервиса после сбоя. Пример: "Среднее время восстановления 1 час."
🟠Время на устранение инцидентов (MTTA - Mean Time to Acknowledge)
Среднее время на принятие мер после обнаружения проблемы. Пример: "Среднее время реакции на инцидент 30 минут."
🟠Процент ошибок (Error Rate)
Процент запросов, завершающихся ошибкой. Пример: "Процент ошибочных запросов не более 0.1%."

🚩Примеры

🟠Доступность
"Сервис был доступен 99.95% времени за последний месяц."
🟠Производительность
"Среднее время отклика сервиса за последний месяц составило 150 миллисекунд."
🟠Время восстановления (MTTR)
"Среднее время восстановления сервиса после сбоев за последний месяц составило 45 минут."
🟠Процент ошибок (Error Rate)
"Процент ошибочных запросов за последний месяц составил 0.05%."

🚩Различие между SLA, SLO и SLI

🟠SLA (Service Level Agreement)
Соглашение между поставщиком услуги и клиентом, включающее SLO и описывающее уровень обслуживания, который должен быть достигнут. SLA может включать последствия за невыполнение SLO.

🟠SLO (Service Level Objective)
Конкретные цели или метрики, которые определяют уровень обслуживания, которого должен достигнуть сервис. SLO являются частью SLA.

🟠SLI (Service Level Indicator)
Конкретные метрики, используемые для измерения фактической производительности сервиса относительно установленных SLO. SLI — это количественные показатели, которые служат основой для SLO.

🚩Важность

🟠Измерение качества услуг
SLI позволяют количественно измерять различные аспекты работы сервиса, такие как доступность и производительность.
🟠Контроль и мониторинг
SLI используются для постоянного мониторинга сервиса и обеспечения соответствия установленным SLO.
🟠Управление производительностью
SLI помогают выявлять проблемы в работе сервиса и принимать меры для их устранения.
🟠Улучшение сервиса
Анализ SLI позволяет улучшать качество обслуживания путем выявления и устранения узких мест и проблем.

🚩Пример использования

🟠Доступность
SLI: "99.9% времени сервис доступен."
SLO: "Сервис должен быть доступен не менее 99.9% времени в течение месяца."
SLA: "Если доступность падает ниже 99.9%, клиент получает компенсацию."
🟠Производительность
SLI: "Среднее время отклика 200 миллисекунд."
SLO: "Среднее время отклика не должно превышать 250 миллисекунд для 95% запросов."
SLA: "Если время отклика превышает 250 миллисекунд, клиент получает компенсацию."

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Где аллоцируется память для горутин?

Каждая горутина получает отдельный стек, который при старте весит около 2 килобайт. Он динамически увеличивается по мере необходимости (до мегабайт), а при простое — может быть сокращён.
Таким образом, память для горутины не аллоцируется в куче сразу, а используется адаптивно.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как каналы устроены в Go?

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

🚩Основы

Могут быть типизированы, что означает, что канал может передавать значения только одного определённого типа. Они могут быть объявлены и инициализированы с помощью ключевого слова chan:
ch := make(chan int) // Канал для передачи значений типа int


🚩Отправка и получение данных

Для отправки значения в канал используется оператор <-:
ch <- 10 // Отправка значения 10 в канал


Для получения значения из канала тот же оператор используется, но в другом контексте:
value := <-ch // Прочитать значение из канала и присвоить его переменной value


🚩Блокировки

Особенностью является то, что операции отправки и получения данных являются блокирующими:
Если горутина пытается отправить данные в канал, она блокируется до тех пор, пока другая горутина не прочитает эти данные.
Аналогично, если горутина пытается прочитать данные из канала, она блокируется до тех пор, пока другая горутина не отправит данные в этот канал.

🚩Буферизация

Могут быть буферизированными, что означает, что они могут хранить ограниченное количество значений без необходимости немедленного получения. Буферизированный канал инициализируется с указанием размера буфера:
ch := make(chan int, 5) // Буферизированный канал с размером буфера 5


В буферизированном канале отправка не блокируется до тех пор, пока буфер не заполнится, и получение не блокируется до тех пор, пока буфер не опустеет.

🚩Закрытие каналов

Каналы можно закрывать, если больше нет необходимости отправлять через них данные. После закрытия канала нельзя отправлять данные, но можно продолжать получать данные до тех пор, пока канал не опустеет:
close(ch)


Проверка на то, что канал закрыт и данные исчерпаны, возможна в операции чтения:
value, ok := <-ch
if !ok {
// Канал закрыт и все данные получены
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какая средняя сложность поиска по слайсу и по Map?

1. Слайс: поиск по индексу имеет сложность O(1), а поиск по значению — O(n), так как требуется линейный перебор элементов.
2. Map: средняя сложность поиска по ключу — O(1) благодаря хешированию, но в худшем случае (при коллизиях) может достигать O(n).


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Чем stream отличается от unary?

В контексте gRPC (Google Remote Procedure Call) unary и stream — это два разных типа взаимодействия между клиентом и сервером.

🚩Unary RPC (Обычный вызов)

🟠Unary (унарный) вызов
это стандартный запрос-ответ:
Клиент отправляет одно сообщение → сервер отвечает одним сообщением.
service UserService {
rpc GetUserInfo(UserRequest) returns (UserResponse);
}


Go-реализация Unary RPC
func (s *server) GetUserInfo(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
user := &pb.UserResponse{
Id: req.Id,
Name: "John Doe",
Email: "john@example.com",
}
return user, nil // Обычный ответ
}


🚩Streaming RPC (Потоковый вызов)

В streaming RPC передача данных идёт потоком. gRPC поддерживает три вида стримов:

🟠Server Streaming (Поток от сервера)
Клиент отправляет один запрос → сервер возвращает поток ответов.
service UserService {
rpc GetUserActivity(UserRequest) returns (stream ActivityResponse);
}


Go-реализация Server Streaming
func (s *server) GetUserActivity(req *pb.UserRequest, stream pb.UserService_GetUserActivityServer) error {
activities := []string{"Login", "Upload File", "Logout"}
for _, activity := range activities {
err := stream.Send(&pb.ActivityResponse{Message: activity})
if err != nil {
return err
}
time.Sleep(time.Second) // Имитация задержки
}
return nil
}


🟠Client Streaming (Поток от клиента)
Клиент отправляет поток данных → сервер отвечает одним ответом.
service UploadService {
rpc UploadFile(stream FileChunk) returns (UploadResponse);
}


Go-реализация Client Streaming
func (s *server) UploadFile(stream pb.UploadService_UploadFileServer) error {
var totalSize int64
for {
chunk, err := stream.Recv()
if err == io.EOF {
return stream.SendAndClose(&pb.UploadResponse{Size: totalSize})
}
if err != nil {
return err
}
totalSize += chunk.Size
}
}


🟠Bidirectional Streaming (Двунаправленный поток)
Клиент и сервер обмениваются данными в потоке одновременно.
service ChatService {
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}


Go-реализация Bi-directional Streaming
func (s *server) Chat(stream pb.ChatService_ChatServer) error {
for {
msg, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
response := &pb.ChatMessage{Text: "Echo: " + msg.Text}
if err := stream.Send(response); err != nil {
return err
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как определить количество символов для строки?

Для определения количества символов в строке в Swift используется свойство .count:
Внимание: .count возвращает количество графемных кластеров, а не байтов или Unicode-кодов. Например, emoji или диакритические символы могут быть представлены несколькими кодовыми точками, но отображаются как один символ.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие функции у map?

Карты (maps) предоставляют несколько основных функций и операций для работы с ними. Эти функции позволяют добавлять, удалять, получать значения и проверять наличие ключей в карте.

🚩Основные операции

🟠Объявление карты
Для этого используется ключевое слово map, после которого указываются типы ключей и значений.
var myMap map[string]int


🟠Инициализация карты
Это можно сделать с помощью функции make или литерала карты.
// Инициализация с помощью make
myMap = make(map[string]int)

// Инициализация с помощью литерала карты
myMap = map[string]int{
"Alice": 25,
"Bob": 30,
}


🟠Добавление и обновление элементов
Для этого используется синтаксис индексирования.
myMap["Charlie"] = 35
myMap["Alice"] = 26 // обновление значения по ключу "Alice"


🟠Извлечение значения по ключу
Для этого используется синтаксис индексирования.
age := myMap["Alice"]
fmt.Println(age) // 26


🟠Проверка существования ключа
Чтобы проверить это можно использовать двойное присваивание.
age, exists := myMap["David"]
if exists {
fmt.Println("Age of David:", age)
} else {
fmt.Println("David not found")
}


🟠Удаление элемента
Для этого используется встроенная функция delete.
delete(myMap, "Bob")


🟠Итерация по карте
Для этого используется цикл for range.
for key, value := range myMap {
fmt.Printf("Key: %s, Value: %d\n", key, value)
}


Пример
package main

import (
"fmt"
)

func main() {
// Инициализация карты с помощью литерала
myMap := map[string]int{
"Alice": 25,
"Bob": 30,
}

// Добавление нового элемента
myMap["Charlie"] = 35

// Обновление существующего элемента
myMap["Alice"] = 26

// Извлечение значения по ключу
age := myMap["Alice"]
fmt.Println("Age of Alice:", age) // 26

// Проверка существования ключа
age, exists := myMap["David"]
if exists {
fmt.Println("Age of David:", age)
} else {
fmt.Println("David not found")
}

// Удаление элемента
delete(myMap, "Bob")

// Итерация по карте
for key, value := range myMap {
fmt.Printf("Key: %s, Value: %d\n", key, value)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое горутины?

Горутины — это легковесные потоки выполнения, которые выполняются параллельно в рамках одного процесса в Go. Они создаются с помощью ключевого слова `go` и могут быть выполнены одновременно, не требуя создания полноценного системного потока. Горутины потребляют меньше ресурсов, чем традиционные потоки, и могут работать асинхронно. Планировщик Go автоматически управляет их выполнением и распределяет между ядрами процессора.

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Почему пустой интерфейс можно привести к любому типу?

В Go пустой интерфейс interface{} является особым типом, который может содержать значение любого типа. Это связано с тем, что в Go любой тип реализует пустой интерфейс, поскольку в нем нет методов, которые нужно реализовать.

🚩Пустой интерфейс

Поскольку пустой интерфейс не требует реализации каких-либо методов, любой тип в Go автоматически реализует этот интерфейс. Это делает пустой интерфейс универсальным контейнером для значений любых типов.
type interface{} interface {}


🚩Внутреннее представление интерфейсов в Go

🟠Type
Типа конкретного значения
🟠Value
Самого значения

Когда значение присваивается переменной типа интерфейс, Go сохраняет информацию о типе и значении этого значения. Для пустого интерфейса эта информация может быть любого типа.

🚩Приведение пустого интерфейса к конкретному типу

Когда значение из пустого интерфейса приводится к конкретному типу, происходит проверка типа во время выполнения. Если значение внутри интерфейса действительно является указанным типом, приведение успешно. В противном случае приведение не удается, и возвращается значение nil или происходит паника, если приведение выполнено без проверки.

Присваивание значений пустому интерфейсу
package main

import "fmt"

func main() {
var i interface{}
i = 42
fmt.Println(i) // 42

i = "hello"
fmt.Println(i) // hello
}


Утверждение типа (Type Assertion)
package main

import "fmt"

func main() {
var i interface{} = "hello"

// Утверждение типа с проверкой
s, ok := i.(string)
if ok {
fmt.Println("String:", s)
} else {
fmt.Println("Not a string")
}

// Утверждение типа без проверки
// Это вызовет панику, если тип не соответствует
s = i.(string)
fmt.Println("String:", s)
}


Использование switch для проверки типа
package main

import "fmt"

func printType(i interface{}) {
switch v := i.(type) {
case string:
fmt.Println("String:", v)
case int:
fmt.Println("Integer:", v)
case bool:
fmt.Println("Boolean:", v)
default:
fmt.Printf("Unknown type: %T\n", v)
}
}

func main() {
printType("hello")
printType(42)
printType(true)
printType(3.14)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Когда и зачем может пригодиться денормализация данных?

Денормализация нужна, когда система испытывает проблемы с производительностью при чтении данных. Особенно полезна, если запросы часто требуют объединения нескольких таблиц. Это снижает нагрузку на базу, уменьшает количество операций соединения и ускоряет выборки — за счёт хранения дублирующих данных.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как работать со слайсами?

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

🚩Что это такое?

Это динамическая последовательность элементов одного типа, которая предоставляет доступ к части или всем элементам массива без копирования данных. Он содержит три компонента:
Указатель на массив.
Длина (количество элементов в слайсе).
Ёмкость (максимальное количество элементов, которые могут быть включены в слайс без перераспределения памяти).

🚩Почему они нужны?

Слайсы позволяют работать с массивами более гибко:
🟠Динамическая длина
В отличие от массивов, длина слайса может изменяться.
🟠Передача данных
Слайсы можно передавать в функции и возвращать из них, не копируя данные.
🟠Удобство работы
Предоставляют множество встроенных функций для работы с последовательностями данных.

🚩Создать слайс можно несколькими способами:

Из массива
    arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // слайс содержит элементы {2, 3, 4}


Используя make
slice := make([]int, 5)  // создаёт слайс длиной и ёмкостью 5, заполненный нулями    


Литерал слайса
slice := []int{1, 2, 3, 4, 5}    


🚩Основные операции

Доступ к элементам
fmt.Println(slice[0])  // выводит первый элемент слайса    


Изменение элементов
slice[1] = 10  // изменяет второй элемент слайса    


Добавление элементов
slice = append(slice, 6, 7)  // добавляет элементы 6 и 7 к слайсу    


Срезка (slicing)
newSlice := slice[1:3]  // создаёт новый слайс с элементами с 1-го по 3-й    


Рассмотрим пример функции, которая добавляет элемент в слайс и возвращает новый слайс
package main

import "fmt"

func main() {
nums := []int{1, 2, 3}
nums = append(nums, 4) // добавление элемента
fmt.Println(nums) // выводит [1, 2, 3, 4]
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM