Golang | Вопросы собесов
4.33K subscribers
26 photos
695 links
Download Telegram
🤔 Что такое указатели?

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
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
🤔 Что такое iota?

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

🚩Основные характеристики

🟠Инициализация с нуля
Начинает счет с 0 в каждой новой группе констант.
🟠Автоматическое увеличение
Каждое последующее использование iota в одной группе констант увеличивает его значение на 1.
🟠Повторное использование
При каждом новом объявлении константного блока iota сбрасывается до 0.

package main

import "fmt"

const (
A = iota // 0
B // 1
C // 2
)

func main() {
fmt.Println(A) // Вывод: 0
fmt.Println(B) // Вывод: 1
fmt.Println(C) // Вывод: 2
}


Использование его для создания битовых флагов
package main

import "fmt"

const (
Flag1 = 1 << iota // 1 << 0 = 1
Flag2 // 1 << 1 = 2
Flag3 // 1 << 2 = 4
Flag4 // 1 << 3 = 8
)

func main() {
fmt.Println(Flag1) // Вывод: 1
fmt.Println(Flag2) // Вывод: 2
fmt.Println(Flag3) // Вывод: 4
fmt.Println(Flag4) // Вывод: 8
}


Сброс его в новом блоке
package main

import "fmt"

const (
X = iota // 0
Y // 1
)

const (
Z = iota // 0 (новый блок констант, iota сбрасывается)
W // 1
)

func main() {
fmt.Println(X) // Вывод: 0
fmt.Println(Y) // Вывод: 1
fmt.Println(Z) // Вывод: 0
fmt.Println(W) // Вывод: 1
}


🚩Комплексное использование

Можно использовать в выражениях и совместно с другими константами для создания более сложных последовательностей.
package main

import "fmt"

const (
_ = iota // пропускаем 0
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20 = 1048576
GB // 1 << 30 = 1073741824
)

func main() {
fmt.Println("KB:", KB) // Вывод: KB: 1024
fmt.Println("MB:", MB) // Вывод: MB: 1048576
fmt.Println("GB:", GB) // Вывод: GB: 1073741824
}


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

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

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

EXPLAIN — это команда в SQL, которая показывает, как база данных выполняет SQL-запрос. Она помогает оптимизировать запросы, анализируя их план выполнения.

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

Когда вы пишете EXPLAIN перед SQL-запросом, база данных не выполняет его, а показывает пошаговый процесс выполнения. Это помогает понять:
Какие индексы используются
Какие таблицы сканируются
Какие соединения (JOIN) выполняются
Оценить производительность запроса
EXPLAIN SELECT * FROM users WHERE age > 30;


Выход (пример)
Seq Scan on users  (cost=0.00..25.00 rows=10 width=100)


🚩`EXPLAIN ANALYZE` — глубокий анализ

Добавляет реальное время выполнения запроса
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 30;


🚩Оптимизация запросов с `EXPLAIN`

Использование индексов
CREATE INDEX idx_users_age ON users(age);


Правильный порядок JOIN
EXPLAIN SELECT * FROM orders JOIN users ON orders.user_id = users.id;


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как расшифровывается аббревиатура ACID?

ACID – это принципы целостности транзакций в базах данных:
- A (Atomicity) – транзакция либо выполняется полностью, либо не выполняется вовсе.
- C (Consistency) – после транзакции БД остается в согласованном состоянии.
- I (Isolation) – параллельные транзакции не влияют друг на друга.
- D (Durability) – данные сохраняются даже в случае сбоя системы.


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

Горутины в Go являются легковесными потоками управления, которые позволяют выполнять параллельные задачи более эффективно по сравнению с потоками операционной системы. Их ключевые преимущества включают:

Легковесность
Горутины потребляют значительно меньше памяти и ресурсов по сравнению с потоками ОС. Каждая горутина стартует с размером стека порядка 2 КБ, тогда как поток ОС требует гораздо большего размера стека (обычно несколько мегабайт).

Планировщик Go
Горутины управляются встроенным планировщиком Go, а не планировщиком ОС. Планировщик Go распределяет выполнение горутин по доступным потокам ОС, используя концепцию "M:N" (много горутин на несколько потоков).

Отсутствие необходимости ручного управления
Создание, синхронизация и управление горутинами намного проще благодаря встроенным средствам Go, таким как каналы (channels) и sync-пакет. В то время как работа с потоками ОС требует дополнительных усилий для синхронизации (мьютексы, условные переменные и т.д.).

Автоматическое управление стеком
Стек горутины автоматически увеличивается или уменьшается в зависимости от потребностей, что позволяет эффективно использовать память.

Простота и удобство
Синтаксис и использование горутин интуитивно понятны. Для запуска достаточно добавить go перед вызовом функции:

package main

import (
"fmt"
"time"
)

func sayHello() {
for i := 0; i < 5; i++ {
fmt.Println("Hello")
time.Sleep(100 * time.Millisecond)
}
}

func main() {
go sayHello() // Запускаем горутину
fmt.Println("World")
time.Sleep(1 * time.Second) // Даем горутине время завершиться
}


Масштабируемость
Горутины позволяют эффективно использовать современные многоядерные процессоры благодаря встроенной конкурентной модели и поддержке параллелизма.

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

Основные структуры данных в Go включают массивы, слайсы, карты (map), структуры (struct) и каналы (channel). Каждая из этих структур имеет свои особенности использования и предназначение.

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

Да, между SSL (Secure Sockets Layer) и TLS (Transport Layer Security) есть отличия. TLS является улучшенной и более безопасной версией SSL.

🚩История и версия

🟠SSL:
SSL 1.0: Никогда не был выпущен публично из-за серьезных уязвимостей.
SSL 2.0: Выпущен в 1995 году, но вскоре был признан небезопасным из-за множества уязвимостей.
SSL 3.0: Выпущен в 1996 году, значительно улучшил безопасность, но со временем также был признан устаревшим из-за уязвимостей (например, POODLE-атака).

🟠TLS:
TLS 1.0: Выпущен в 1999 году как обновление SSL 3.0. Включает исправления безопасности и улучшения.
TLS 1.1: Выпущен в 2006 году с дополнительными защитами от некоторых атак.
TLS 1.2: Выпущен в 2008 году, поддерживает современные алгоритмы шифрования и хеширования.
TLS 1.3: Выпущен в 2018 году, значительно улучшена безопасность и производительность, упрощен процесс установки соединения.

🚩Технические отличия

🟠Алгоритмы шифрования:
SSL: Поддерживает более старые и менее безопасные алгоритмы шифрования.
TLS: Поддерживает более современные и безопасные алгоритмы шифрования. TLS 1.3 исключает поддержку устаревших алгоритмов и предлагает только современные безопасные алгоритмы.

🟠Процесс рукопожатия (Handshake):
SSL: Более сложный процесс рукопожатия, включающий несколько шагов, что делает его уязвимым для некоторых атак.
TLS: Улучшенный процесс рукопожатия, включая использование HMAC (Hash-based Message Authentication Code) для обеспечения целостности сообщения. TLS 1.3 значительно упрощает и ускоряет процесс рукопожатия.

🟠Целостность данных:
SSL: Использует комбинацию MD5 и SHA-1 для целостности данных, что не так безопасно по современным стандартам.
TLS: Использует HMAC с SHA-256 и другими современными алгоритмами для обеспечения целостности данных.

🟠Управление сеансами:
SSL: Меньше возможностей для управления сеансами.
TLS: Включает улучшенные механизмы для управления сеансами, такие как возобновление сеансов, что позволяет экономить время и ресурсы при повторных подключениях.

🚩Безопасность

🟠SSL: Уязвим для ряда атак, таких как POODLE, BEAST и другие, из-за устаревших и менее безопасных методов шифрования и проверки целостности.
🟠TLS: Значительно более безопасен благодаря устранению уязвимостей SSL и внедрению современных методов шифрования и проверки целостности. TLS 1.3 еще более безопасен благодаря исключению устаревших методов и упрощению процесса рукопожатия.

🚩Совместимость

🟠SSL: Практически не используется в современных системах из-за известных уязвимостей и устаревших методов безопасности.
🟠TLS: Широко поддерживается и используется в современных браузерах, серверах и других сетевых устройствах. TLS 1.2 и TLS 1.3 являются наиболее часто используемыми версиями.

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

Пустая строка ("") в Go — это структура string, которая содержит:
- указатель на данные (0 байт),
- длину строки (int — 8 байт на 64-битной архитектуре).
Таким образом, даже пустая строка весит 16 байт (указатель + длина), без учёта хранимых данных.


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

Слайсы являются ссылочными типами, поэтому простое присваивание одного слайса другому создаст новую ссылку на тот же подлежащий массив. Если вы хотите создать копию слайса с независимым подлежащим массивом, можно использовать встроенную функцию copy или методы, такие как использование append.

🚩Способы копирования слайса

🟠Использование функции `copy`
Создает побайтовую копию элементов из одного слайса в другой.
package main

import "fmt"

func main() {
original := []int{1, 2, 3, 4, 5}

// Создаем новый слайс той же длины, что и оригинал
copySlice := make([]int, len(original))

// Копируем элементы из оригинального слайса в новый
copy(copySlice, original)

// Изменяем элемент в копии
copySlice[0] = 100

fmt.Println("Оригинал:", original) // Выводит: Оригинал: [1 2 3 4 5]
fmt.Println("Копия:", copySlice) // Выводит: Копия: [100 2 3 4 5]
}


Использование функции
inal)   


Чтобы создать новый слайс с копированными элементами.
package main

import "fmt"

func main() {
original := []int{1, 2, 3, 4, 5}

// Копируем элементы из оригинального слайса в новый слайс
copySlice := append([]int(nil), original...)

// Изменяем элемент в копии
copySlice[0] = 100

fmt.Println("Оригинал:", original) // Выводит: Оригинал: [1 2 3 4 5]
fmt.Println("Копия:", copySlice) // Выводит: Копия: [100 2 3 4 5]
}


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

- Лёгкие по памяти (обычно килобайты, а не мегабайты).
- Масштабируемы — можно запускать тысячи горутин.
- Управляются внутренним планировщиком Go, а не ОС.
- Быстро переключаются между задачами.
- Просты в использовании (каналы, select, go).
Это делает горутины особенно эффективными для серверной разработки и высоконагруженных систем.


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

Замыкание (closure) — это функция, которая запоминает и использует переменные из своей внешней области видимости, даже если эта область видимости уже закончила своё выполнение.

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

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

import "fmt"

func counter() func() int {
count := 0
return func() int {
count++
return count
}
}

func main() {
inc := counter() // создаём замыкание
fmt.Println(inc()) // 1
fmt.Println(inc()) // 2
fmt.Println(inc()) // 3

newInc := counter() // новое замыкание с новой переменной count
fmt.Println(newInc()) // 1
}


🚩Где полезно использовать?

Счётчики (как в примере выше). Фабричные функции, создающие специфичные обработчики. Ограничение доступа к данным (инкапсуляция).

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Зачем нужен foreign key?

Foreign key (внешний ключ) — это ограничение в базе данных, обеспечивающее связь между таблицами и целостность данных.
Он:
- Предотвращает вставку «висячих» записей (без связанных данных).
- Позволяет БД контролировать каскадное удаление/обновление.
- Делает структуру БД более безопасной и логичной.


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

HAVING — это оператор SQL, который фильтрует результаты после GROUP BY, аналогично WHERE, но работает с агрегатными функциями (COUNT(), SUM(), AVG(), MAX(), MIN()).

🚩Ключевая разница

WHERE фильтрует до GROUP BY (по отдельным строкам).
HAVING фильтрует после GROUP BY (по сгруппированным данным).

Пример 1: Фильтрация по HAVING
Задача: Вывести товары, у которых продано более 10 единиц.
SELECT product, SUM(quantity) as total_sold
FROM sales
GROUP BY product
HAVING SUM(quantity) > 10;


Пример 2: Разница между WHERE и HAVING
SELECT category, COUNT(*) as total_products
FROM products
WHERE price > 100 -- Убирает дешёвые товары ДО группировки
GROUP BY category
HAVING COUNT(*) > 5; -- Оставляет только категории с более 5 товаров


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что известно про планирование goroutine

Планирование горутин управляется M:N моделью, где:
- G (goroutine) — лёгкая задача.
- M (machine) — системный поток.
- P (processor) — логическая единица исполнения, выполняющая G на M.
Планировщик:
- Назначает горутины на доступные процессоры.
- Переключает задачи при блокировке.
- Использует стратегию work-stealing, чтобы равномерно загружать потоки.
- Работает независимо от ОС.


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


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

🚩Композиция

Позволяет включать одну структуру в другую, что дает возможность использовать методы встроенной структуры. Это часто называют "встраиванием" или "композицией" вместо наследования.
package main

import "fmt"

// Определение структуры
type Engine struct {
Power int
}

func (e Engine) Start() {
fmt.Println("Engine started with power:", e.Power)
}

// Определение другой структуры, которая включает первую
type Car struct {
Brand string
Engine
}

func main() {
myCar := Car{
Brand: "Toyota",
Engine: Engine{Power: 150},
}

fmt.Println("Car brand:", myCar.Brand)
myCar.Start() // Вызов метода встроенной структуры
}


🚩Интерфейсы

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

import "fmt"

// Определение интерфейса
type Drivable interface {
Drive()
}

// Определение структуры, реализующей интерфейс
type Car struct {
Brand string
}

func (c Car) Drive() {
fmt.Println(c.Brand, "is driving")
}

// Еще одна структура, реализующая интерфейс
type Bike struct {
Brand string
}

func (b Bike) Drive() {
fmt.Println(b.Brand, "is driving")
}

// Функция, принимающая интерфейс
func StartDriving(d Drivable) {
d.Drive()
}

func main() {
car := Car{Brand: "Toyota"}
bike := Bike{Brand: "Yamaha"}

StartDriving(car)
StartDriving(bike)
}


🚩Встраивание интерфейсов

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

import "fmt"

// Определение базового интерфейса
type Printer interface {
Print()
}

// Определение другого интерфейса, включающего первый
type AdvancedPrinter interface {
Printer
Scan()
}

// Реализация структуры, реализующей расширенный интерфейс
type MultiFunctionPrinter struct{}

func (m MultiFunctionPrinter) Print() {
fmt.Println("Printing...")
}

func (m MultiFunctionPrinter) Scan() {
fmt.Println("Scanning...")
}

func main() {
mfp := MultiFunctionPrinter{}
mfp.Print()
mfp.Scan()
}


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

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


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

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

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

Горутины (goroutines) — лёгкие потоки, управляемые Go.
Планировщик (scheduler) — распределяет горутины по потокам ОС.
Модель памяти — управление стеком и кучей.
Сборщик мусора (GC) — автоматически очищает неиспользуемую память.
Syscalls — взаимодействие с ОС.

🚩Горутины и планировщик (scheduler)

Горутины легче, чем потоки ОС: Go может запускать миллионы горутин, а планировщик (M:N) распределяет их по потокам ОС.
Go использует M:N-модель, где:
- M — системные потоки (OS threads).
- N — горутины (goroutines).
- P — процессоры (logical processors) — управляют выполнением горутин.
P привязан к M, а G (горутины) распределяются между `P` динамически.
Это позволяет Go эффективно использовать CPU без ручного управления потоками.
Goroutine (G1, G2, G3, ...) 

Scheduler → P (Processor) → M (OS Thread) → CPU


🚩Управление памятью и стеком

Стек (stack) в Go динамический — он начинается с 2 KB и увеличивается при необходимости.
Если стек заполнен, Go автоматически расширяет его.
В отличие от C, Go не требует malloc/free, так как есть GC (garbage collector).

🚩Сборщик мусора (Garbage Collector)

Go использует автоматический сборщик мусора, который:
Работает параллельно с программой
Минимизирует паузы (low-latency GC)
Очищает неиспользуемые объекты из кучи
import "runtime"

runtime.GC() // Принудительный запуск сборщика мусора (обычно не нужно)


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

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


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