Golang | Вопросы собесов
4.72K subscribers
30 photos
964 links
Download Telegram
🤔 Как устроен слайс в Go ?

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

🚩Структура

🟠Указатель на базовый массив
Указатель на первый элемент массива, на который ссылается слайс.
🟠Длина (length)
Количество элементов, доступных в слайсе.
🟠Емкость (capacity)
Максимальное количество элементов, которое может содержать слайс без перераспределения памяти.

🚩Внутреннее устройство

Можно представить в виде структуры
type slice struct {
ptr *ElementType // Указатель на базовый массив
len int // Длина
cap int // Емкость
}


🟠Слайс из массива
Можно создать из массива, указав диапазон элементов:
package main

import "fmt"

func main() {
arr := [5]int{1, 2, 3, 4, 5}
slice := arr[1:4] // Слайс содержит элементы с индексами 1, 2, 3
fmt.Println(slice) // [2 3 4]
}


🟠Слайс с использованием функции make
Позволяет создать слайс определенной длины и емкости:
package main

import "fmt"

func main() {
slice := make([]int, 3, 5) // Слайс длиной 3 и емкостью 5
fmt.Println(slice) // [0 0 0]
}


🟠Доступ к элементам слайса
Осуществляется так же, как и к элементам массива:
package main

import "fmt"

func main() {
slice := []int{1, 2, 3, 4, 5}
fmt.Println(slice[0]) // 1
slice[1] = 10
fmt.Println(slice) // [1 10 3 4 5]
}


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

🟠Добавление элементов
Для этого используется функция append
package main

import "fmt"

func main() {
slice := []int{1, 2, 3}
slice = append(slice, 4, 5) // Добавляем элементы 4 и 5 в конец слайса
fmt.Println(slice) // [1 2 3 4 5]
}


🟠Извлечение подмножества слайса (slicing)
Можно создавать новые слайсы на основе существующих
package main

import "fmt"

func main() {
slice := []int{1, 2, 3, 4, 5}
subSlice := slice[1:4] // Слайс содержит элементы с индексами 1, 2, 3
fmt.Println(subSlice) // [2 3 4]
}


🟠Копирование слайсов
Для этого используется функция copy
package main

import "fmt"

func main() {
src := []int{1, 2, 3}
dst := make([]int, len(src))
copy(dst, src)
fmt.Println(dst) // [1 2 3]
}


🚩Динамическое изменение длины и емкости

Могут автоматически изменять свою емкость при добавлении новых элементов с помощью append. Когда емкость текущего массива недостаточна для добавления новых элементов, создается новый массив с большей емкостью, в который копируются существующие элементы.
package main

import "fmt"

func main() {
slice := make([]int, 3, 5)
fmt.Println("Before append:", slice, "Len:", len(slice), "Cap:", cap(slice))

slice = append(slice, 1, 2, 3)
fmt.Println("After append:", slice, "Len:", len(slice), "Cap:", cap(slice))

slice = append(slice, 4)
fmt.Println("After another append:", slice, "Len:", len(slice), "Cap:", cap(slice))
}


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

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Можно ли сделать int(string) и string(int) соответственно?

Существуют встроенные функции для преобразования типов, включая преобразование из строки в целое число и наоборот. Для этих целей используются функции из стандартной библиотеки strconv. Рассмотрим, как это делается.

🚩Преобразование строки в целое число

Для этого используется функция strconv.Atoi. Она возвращает два значения: само число и ошибку, если преобразование не удалось.
package main

import (
"fmt"
"strconv"
)

func main() {
str := "123"
num, err := strconv.Atoi(str)
if err != nil {
fmt.Println("Error converting string to int:", err)
} else {
fmt.Println("Converted number:", num)
}
}


🚩Преобразование целого числа в строку

Для этого используется функция strconv.Itoa.
package main

import (
"fmt"
"strconv"
)

func main() {
num := 123
str := strconv.Itoa(num)
fmt.Println("Converted string:", str)
}


🚩Обработка ошибок при преобразовании

Важно обрабатывать ошибки при преобразовании типов, особенно при преобразовании строки в целое число, чтобы избежать неожиданных сбоев в программе.
package main

import (
"fmt"
"strconv"
)

func main() {
str := "abc"
num, err := strconv.Atoi(str)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Converted number:", num)
}
}


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

Деление int на 0 вызовет паническую ошибку, а деление float на 0 вернёт +Inf, -Inf или NaN в зависимости от ситуации.

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

Линтеры — это инструменты для автоматической проверки кода на ошибки, потенциальные баги и несоответствие стилю кодирования.

🟠Популярные линтеры для Go
В Go есть несколько популярных линтеров:
golangci-lint – самый мощный и популярный, объединяет множество линтеров.
go vet – стандартный инструмент для поиска ошибок.
golint – проверяет стиль кода (но устарел).
staticcheck – анализирует код на ошибки и неэффективность.

🟠Установка линтера
Устанавливаем golangci-lint (лучший вариант)
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest


После установки проверьте версию:
golangci-lint --version


🟠Запуск линтера
Запустить проверку в проекте можно так:
golangci-lint run


Можно проверить только определённый файл:
golangci-lint run myfile.go


Если хотите автоматически исправлять ошибки, используйте:
golangci-lint run --fix


🟠Использование `go vet` (встроенный анализатор)
Go уже имеет встроенный линтер
go vet ./...


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

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


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

В Go эффективное объединение строк – важная задача, поскольку строки неизменяемые. Неправильный подход (например, простая конкатенация s1 + s2 + s3) может привести к множественным аллокациям памяти и копированиям.

🚩Способы объединения строк

Использование strings.Builder (Рекомендуется)
Это самый эффективный способ склеивания строк, так как он минимизирует количество аллокаций.
package main

import (
"fmt"
"strings"
)

func main() {
var sb strings.Builder

sb.WriteString("Hello")
sb.WriteString(", ")
sb.WriteString("World!")

result := sb.String()
fmt.Println(result) // Hello, World!
}


Использование + (Неэффективно )
s := "Hello" + ", " + "World!"


Использование fmt.Sprintf (Неэффективно )
s := fmt.Sprintf("%s, %s!", "Hello", "World")


Использование strings.Join (Хорошо для срезов )
Если строки хранятся в []string, strings.Join – это оптимальный вариант
package main

import (
"fmt"
"strings"
)

func main() {
words := []string{"Hello", "World", "Go"}
result := strings.Join(words, ", ")
fmt.Println(result) // Hello, World, Go
}


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

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

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

Когда сервер тормозит, важно быстро найти причину. Разберём основные направления диагностики.

🟠Логи (логирование)
Сначала проверяем логи!
Системные логи (journalctl -u myservice, /var/log/syslog, /var/log/messages)
Логи приложения (например, logs/app.log)
Nginx / Apache (/var/log/nginx/error.log, /var/log/httpd/error.log)
Пример просмотра последних 100 строк лога:
tail -n 100 /var/log/syslog


Если используете docker:
docker logs my_container --tail 100 -f


🟠Нагрузка на процессор и память
Проверяем, не перегружен ли сервер:
top
htop # Более удобный интерфейс


Или просто:
ps aux --sort=-%cpu | head -10  # Топ-10 процессов по CPU
ps aux --sort=-%mem | head -10 # Топ-10 процессов по памяти


Если процесс "myapp" жрет CPU, смотрим его нагрузку:
pidstat -p $(pgrep myapp)


🟠Сетевые проблемы
Проверяем сетевую активность:
netstat -tulnp  # Слушающие порты
ss -tulnp # Альтернатива netstat


Пингуем сервер:
ping google.com


Проверяем скорость соединения:
wget --output-document=/dev/null https://speedtest.tele2.net/10MB.zip


🟠Нагрузка на диск
Проверяем, не забит ли диск:
df -h   # Свободное место
du -sh /* | sort -h # Топ занимаемых мест


Мониторинг активности диска:
iotop  # Если доступен


🟠База данных
Если сервер использует PostgreSQL / MySQL, смотрим запросы:
SHOW PROCESSLIST;  # MySQL: текущие запросы
SELECT * FROM pg_stat_activity; # PostgreSQL: активные запросы


Проверяем медленные запросы (если включен slow_query_log):
tail -n 100 /var/log/mysql/slow.log


---

🟠Go-профилирование (если сервер на Go)
Включаем pprof и анализируем:
import _ "net/http/pprof"
go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }()


Запускаем профайлер:
go tool pprof https://localhost:6060/debug/pprof/profile?seconds=30


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

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


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

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

🚩Неизменяемость

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

🚩Структура строки

Внутренне строка представлена структурой, которая содержит два поля:
Указатель на массив байтов: Это указатель на первый элемент массива байт, который фактически хранит символы строки в кодировке UTF-8.
Длина: Количество байт в строке, а не количество рун или символов. Это важное различие, поскольку в UTF-8 один символ может занимать от 1 до 4 байт.

🚩UTF-8 как стандартная кодировка

Go использует UTF-8 как стандартную кодировку для строк. Это позволяет эффективно работать с международным текстом, поддерживая широкий спектр символов без сложностей, связанных с другими кодировками. Однако это также означает, что операции, такие как получение длины строки в рунах (символах) или доступ к отдельному символу, могут потребовать дополнительных вычислений для обработки многобайтовых символов.

🚩Срезы строк

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

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

Благодаря неизменяемости и способу хранения строк в виде срезов байтов, Go обеспечивает эффективное управление памятью и производительность при работе со строками. Однако необходимо быть осторожным с операциями, которые могут казаться невинными, но приводят к частому созданию новых строк, так как это может повлечь за собой издержки на выделение памяти и сборку мусора.
s := "Hello, world"      // Создание строки
t := s[7:] // Срез строки, создает новую строку "world"

fmt.Println(s) // Выводит: Hello, world
fmt.Println(t) // Выводит: world


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

Такая структура называется значимым типом (value type).
Когда ты передаёшь её в функцию или присваиваешь другой переменной, создаётся копия значения, а не ссылка. В Go к value types относятся: int, float, bool, struct, array.


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

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

🚩Проверка размера в Go (`unsafe.Sizeof`)

Можно проверить размер типа с помощью unsafe.Sizeof()
package main

import (
"fmt"
"unsafe"
)

func main() {
var a int64
var b float64
var c byte // то же самое, что uint8
fmt.Println("int64:", unsafe.Sizeof(a)) // 8
fmt.Println("float64:", unsafe.Sizeof(b)) // 8
fmt.Println("byte:", unsafe.Sizeof(c)) // 1
}


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

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


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🤔 Как каналы устроены в 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
👍2
🤔 В чем отличия HTTP 1.1 и HTTP 2?

HTTP/2 поддерживает мультиплексирование запросов, бинарный формат и сжатие заголовков, что делает его быстрее, чем текстовый и последовательный HTTP/1.1.

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

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

🚩Императивное программирование

Это стиль программирования, где выражается последовательность команд для выполнения задач. В этом подходе программист указывает машине, как изменять своё состояние пошагово, контролируя поток выполнения через управляющие конструкции, такие как циклы, условные операторы и т.д.
🟠Подробное описание процесса
Программист должен указывать все шаги, которые необходимо выполнить для достижения результата.
🟠Примеры языков
Java, C, Python в их традиционном использовании.
🟠Управление состоянием
Императивные программы часто включают явное управление состоянием и его изменениями.
# Императивный подход к сортировке массива методом пузырька
def bubble_sort(array):
n = len(array)
for i in range(n):
for j in range(0, n-i-1):
if array[j] > array[j+1]:
array[j], array[j+1] = array[j+1], array[j]
return array


🚩Декларативное программирование

Это стиль программирования, где описывается желаемый результат, но не детализируется процесс его достижения. В декларативном стиле программа определяет, что должно быть сделано, а не как.
🟠Абстракция от процесса
Описываются желаемые свойства результата, а система сама определяет, как его достичь.
🟠Примеры языков
SQL, HTML, CSS, функциональные языки программирования, такие как Haskell.
🟠Сокрытие сложности
Декларативный подход часто предполагает высокий уровень абстракции, что уменьшает количество деталей, которые нужно учитывать.

-- Декларативный запрос в SQL для получения списка сотрудников, отсортированного по зарплате
SELECT name, salary FROM employees ORDER BY salary DESC;


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

🟠Описание vs. результат
Императивное программирование фокусируется на описании шагов, необходимых для достижения результата, в то время как декларативное программирование описывает желаемый результат без спецификации конкретных шагов.

🟠Управление состоянием
Императивный подход требует активного управления состоянием программы, в то время как в декларативном подходе состояние управляется системой или вовсе абстрагировано.

🟠Поддержка языка
Разные языки поддерживают разные стили программирования. Некоторые языки, как JavaScript, могут поддерживать оба стиля в зависимости от использования.

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

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

Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1
🤔 Какие функции у 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
👍3
🤔 Как устроены горутины, сколько памяти они занимают в стеке?

Горутины — это легковесные потоки, управляемые рантаймом Go, а не ОС.
Каждая горутина:
- Получает начальный стек размером ~2 КБ.
- Имеет собственные регистры состояния и структуру для планирования.
- Может перераспределять стек по мере роста (growable stack), что экономит ресурсы.
Благодаря этому в Go можно создать десятки или сотни тысяч горутин, в отличие от потоков ОС.


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