Библиотека Go для собеса | вопросы с собеседований
6.87K subscribers
218 photos
6 videos
1 file
417 links
Вопросы с собеседований по Go и ответы на них.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/0b524a15

Для обратной связи: @proglibrary_feeedback_bot

Наши каналы: https://t.iss.one/proglibrary/9197
Download Telegram
Какие задачи можно решить с помощью конструкции ‘…’ в Go

Синтаксическая конструкция ‘…’ в Go используется в нескольких контекстах, в частности для:

1️⃣Определение функции с переменным количеством аргументов (вариативная функция)
Она позволяет функции принимать неопределенное количество аргументов одного типа.
Синтаксис: параметр функции определяется с использованием ... перед типом.
func sum(nums ...int) int {
total := 0
for _, num := range nums {
total += num
}
return total
}


2️⃣Передача среза как отдельных аргументов функции
Когда вы хотите передать все элементы среза как отдельные аргументы функции, используется ... .
Синтаксис: при вызове функции с срезом используется ... после имени среза.
nums := []int{1, 2, 3, 4}
fmt.Println(sum(nums...)) // Вывод: 10


3️⃣Определение массивов с неизвестной заранее длиной
Когда необходимо создать массив, длина которого будет определена на основе количества элементов.
Синтаксис: при инициализации массива используется ... вместо явного указания длины.
x := [...]int{1, 2, 3}// Go определит длину массива автоматически


🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍112
Какие особенности связаны с неиспользуемыми переменными и импортами в Go

1️⃣Неиспользуемая локальная переменная

• Если вы объявляете локальную переменную, но не используете её в коде, это приведет к ошибке компиляции.
package main

import "fmt"

func main() {
x := 10
fmt.Println("Hello, World!")
}

Компилятор выдаст ошибку: x declared but not used

2️⃣Неиспользуемое возвращаемое значение

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

package main

import "errors"

func doSomething() (int, error) {
return 42, errors.New("an error occurred")
}

func main() {
doSomething() // Это допустимо
result, err := doSomething() //
}

Это вызовет ошибку, так как result и err не используются

3️⃣Неиспользуемый импорт

• Когда вы импортируете пакет, но не используете его, компилятор также выдаст ошибку.
package main

import "math"

func main() {
// Ничего не делаем
}

Компилятор выдаст ошибку: "math" imported and not used.

Исключения

• Это правило не касается неиспользуемых параметров функций или глобальных переменных. Они могут быть не использованы, но код всё равно скомпилируется.
package main

import "fmt"

var globalVar int

func greet(name string, age int) {
fmt.Println("Hello,", name)
}

func main() {
greet("Alice", 25)
fmt.Println("Hello, World!")
}

Глобальная переменная globalVar не используется, но код скомпилируется без ошибок.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Что такое обратный индекс и где его используют

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

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

Пример структуры данных для обратного индекса в Go:
package main

import "fmt"

type InvertedIndex map[string][]int

func addToIndex(index InvertedIndex, term string, docID int) {
index[term] = append(index[term], docID)
}

func main() {
index := make(InvertedIndex)

// Добавление в индекс
addToIndex(index, "hello", 1)
addToIndex(index, "world", 1)
addToIndex(index, "go", 2)

// Печать результата
fmt.Println(index)
}


В этом примере создается обратный индекс, где слово «hello» встречается в документе 1, «world» — в документе 1, а «go» — в документе 2.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥1
Как вам вопросы прошедшей недели

Оцените их по шкале 🔥,❤️,👍,😢, 🥱,
где 🔥 — это супер, а 🥱 — это скучно.

Также приветствуется фидбек в комментах.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22🔥13🥱84
This media is not supported in your browser
VIEW IN TELEGRAM
Добейте 7 бустов, плиз, иначе вопросов с собесов не будет 🚬
Please open Telegram to view this post
VIEW IN TELEGRAM
😢13😁5
Что такое паттерн «Декоратор» и как его реализовать

Декоратор — это способ добавить новую логику к объекту, не меняя его код. В Go это удобно делать через интерфейсы. Один объект «оборачивает» другой и выполняет дополнительную работу до или после вызова.

Такой подход часто используют для логирования, замеров времени, прав доступа и т.д.

Простой пример: логирование вызова метода
type Handler interface {
Handle(msg string) string
}

type SimpleHandler struct{}

func (h SimpleHandler) Handle(msg string) string {
return "Handled: " + msg
}


Теперь сделаем обёртку:
type LoggingHandler struct {
next Handler
}

func (l LoggingHandler) Handle(msg string) string {
fmt.Println("Запрос:", msg)
result := l.next.Handle(msg)
fmt.Println("Ответ:", result)
return result
}


Использование:
func main() {
base := SimpleHandler{}
withLog := LoggingHandler{next: base}

fmt.Println(withLog.Handle("тест"))
}


🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
17🔥3👍2
В чем особенность неиспользуемых переменных и импортов в Go

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

➡️ Если в коде есть неиспользуемая локальная переменная, такой код не скомпилируется.
package main

import "fmt"

func main() {
x := 10
fmt.Println("Hello, World!")
}

Компилятор выдаст ошибку: x declared but not used

➡️ Если функция возвращает значения, но значения не используются, компилятор не выдаст ошибку.
Однако если значения присваиваются переменным, которые затем не используются, это вызовет ошибку компиляции.
package main

import "errors"

func doSomething() (int, error) {
return 42, errors.New("an error occurred")
}

func main() {
doSomething() // Это допустимо
result, err := doSomething() // Ошибка, так как result и err не используются
}


➡️ Если в коде присутствует импорт, который не используется, компилятор выдаст ошибку: "math" imported and not used
package main

import "math" // Ошибка, импорт не используется

func main() {
// Ничего не делаем
}


Правило не касается неиспользуемых параметров функций и глобальных переменных. Код скомпилируется:
package main

import "fmt"

var globalVar int // Глобальная переменная, которая не используется

func greet(name string, age int) { // Параметры функции также могут не использоваться
fmt.Println("Hello,", name)
}

func main() {
greet("Alice", 25) // Параметры функции 'name' и 'age' не используются
fmt.Println("Hello, World!")
}


🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👾5👍42🥱1
Если вы зашьёте в код приватный токен и скомпилируете код — можно ли будет извлечь токен

Когда вы компилируете исходный код в бинарный файл, все данные, включая строки, токены, конфигурации, остаются в самом бинарном файле.

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

Например, инструменты вроде strings могут быть использованы для поиска строк в бинарных файлах.

Так что да, токен можно будет прочитать.

На практике, токены или пароли должны быть хранены в защищённых местах, например:

• В переменных окружения.

• В защищённых хранилищах секретов, например, HashiCorp Vault, AWS Secrets Manager, Azure Key Vault.

• Шифрованных конфигурационных файлах.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍72
Как создать простой веб-сервер на Go с использованием стандартной библиотеки

Базовый пример:
package main

import (
"fmt"
"net/http"
)

func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, World!")
}

func main() {
http.HandleFunc("/hello", helloHandler)
http.ListenAndServe(":8080", nil)
}


1️⃣ Подключаем fmt для форматированного вывода и net/http для работы с HTTP.

2️⃣ Обработчик запросов: функция helloHandler отвечает на все запросы сообщением "Hello, World!".

3️⃣ С помощью http.HandleFunc регистрируем обработчик на корневой эндпоинт ("/hello").

4️⃣ http.ListenAndServe(":8080", nil) запускает сервер на порту 8080.

➡️ После запуска кода при переходе по адресу https://localhost:8080/hello вы получите ответ: "Hello, World!"

Также можно использовать http.FileServer:
package main

import "net/http"

func main() {
port := ":8080"
handler := http.FileServer(http.Dir("."))
http.ListenAndServe(port, handler)
}

Аналог python -m http.serverраздаёт текущую директорию по HTTP.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
😁52
Чем является rune в Go и как он связан с символами Unicode

Работа со строками в Go не ограничивается ASCII-символами, в текстах часто встречаются символы других языков и эмодзи. Для корректной обработки таких символов используется тип rune.

rune — это псевдоним для int32, предназначенный для хранения Unicode-кода одного символа.

При переборе строки с помощью конструкции for range, Go обходит строку по символам Unicode, автоматически преобразуя её в последовательность rune.

Примеры:

Объявление и инициализация руны:
var r rune = 'A'


Преобразование строки в срез рун:
s := "Привет"
runes := []rune(s)


Итерация по рунам в строке:
for _, r := range "Привет" {
fmt.Printf("%c ", r)
}
// Вывод: П р и в е т


Обратное преобразование среза рун в строку:
runes := []rune{'П', 'р', 'и', 'в', 'е', 'т'}
s := string(runes) // "Привет"


Получение Unicode-кода руны:
r := 'A'
code := int32(r) // 65


Проверка длины строки в рунах:
s := "Привет"
length := utf8.RuneCountInString(s) // 6


🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍102🔥1
Как Go реализует обработку ошибок

1️⃣ Явная обработка — в Go нет исключений.
Функции, которые могут завершиться с ошибкой, возвращают её как отдельное значение наряду с результатом.


2️⃣ Множественные возвращаемые значения — стандартный шаблон:
val, err := someFunction()
if err != nil {
// обработка ошибки
}


3️⃣ Кастомные ошибки — через пакет errors можно определять собственные типы ошибок и добавлять к ним полезную информацию.

4️⃣ Обогащение контекста — с Go 1.13 доступны errors.Is, errors.As и fmt.Errorf(... %w ...) для оборачивания ошибок с сохранением исходной причины.
func DoSomething() error {
if err := someOperation(); err != nil {
return fmt.Errorf("someOperation failed: %w", err)
}
return nil
}


5️⃣ Нет finally — очистка ресурсов выполняется через defer.

6️⃣ Panic и recover — используются только для критических непредвиденных ситуаций, например выхода за границы массива.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍2🔥1
Расскажите о механизме мьютексов в Go и укажите существующие типы

Mutex (mutual exclusion — взаимное исключение) — примитив синхронизации для защиты критической секции программы. Он предотвращает гонки данных и deadlock’и, обеспечивая эксклюзивный доступ к ресурсу.

Критическая секция — участок кода, где одновременно может работать только одна горутина.

➡️ sync.Mutexэксклюзивная блокировка
Только одна горутина может захватить мьютекс и получить доступ к ресурсу.
var mu sync.Mutex

func increment() {
mu.Lock()
count++
mu.Unlock()
}

Здесь мы рассматриваем защиту глобальной переменной count при инкременте из множества горутин.

➡️ sync.RWMutex концептуально то же самое, что и Mutex, но дает вам немного больше контроля над памятью.
Позволяет:
RLock — множеству горутин читать одновременно.
Lock — только одному писателю, без читателей.
var mu sync.RWMutex

func set(key, value string) {
mu.Lock()
cache[key] = value
mu.Unlock()
}

func get(key string) string {
mu.RLock()
defer mu.RUnlock()
return cache[key]
}

Дает безопасный доступ к кэшу — много читателей, один писатель.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥1👏1
Что собой представляет «затенение» переменной (shadowing) в Go

В Go «затенение» (shadowing) происходит, когда переменная, объявленная в локальной области видимости, имеет такое же имя, как и переменная во внешней области видимости. Внутренняя переменная «затеняет» внешнюю, делая её недоступной.
package main

import (
"fmt"
)

func main() {
x := 10

if true {
x := 5 // затенение внешней переменной x
fmt.Println(x) // выводит 5
}

fmt.Println(x) // выводит 10
}


«Затенение» может быть особенно запутывающим, когда оно касается переменных, полученных из результатов функций, таких как err. Пример часто встречающегося кода в Go:
value, err := someFunction()
if err != nil {
// обработка ошибки
}

// ...

value2, err := anotherFunction() // новое затенение переменной err
if err != nil {
// обработка ошибки
}


Если использовать := вместо =, вы создадите новую переменную err, которая «затенит» переменную err из внешней области видимости. Это может привести к неправильной обработке ошибок, так как внешняя ошибка будет проигнорирована.

Обнаружение затенения:

1. go vet -shadow ./...
2. golangci-lint run --enable shadow
3. Использование инструмента shadow:
$ go get -u golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
$ go vet -vettool=$(which shadow)


🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2🔥1
Как минимизировать использование памяти в Go при работе с большими структурами данных

➡️ Избегайте глобальных переменных, они остаются в памяти на весь срок работы программы.

➡️ Выбирайте правильные типы данных используйте int8, int16 вместо int, если диапазон позволяет.

➡️ Используйте sync.Pool для повторного использования часто создаваемых объектов.

➡️ Ленивая инициализация, инициализируйте данные только по мере необходимости.

➡️ Передавайте указатели на структуры, это экономит память по сравнению с копиями.

➡️ Срезы против массивов — используйте массивы, если размер данных известен заранее.

➡️ Освобождение ресурсов, присваивайте nil неиспользуемым структурам.

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

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

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥1
Что включает в себя набор примитивов синхронизации пакета sync в Go

Примитивы синхронизации — инструменты (в основном из пакета sync), которые обеспечивают безопасную работу с общими данными и координацию горутин.

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

sync.RWMutex — вариант мьютекса с разделением на блокировку для чтения и записи:
Одновременно могут выполняться несколько операций чтения.
Запись возможна только при полном исключении доступа.

sync.WaitGroup — позволяет дождаться завершения группы горутин. Удобен для синхронного старта/остановки процессов.

sync.Once — гарантирует однократное выполнение кода, даже при множественных вызовах из разных горутин.

sync.Cond — условная переменная, при помощи которой горутина может "заснуть" до наступления определённого события.

Go предлагает два подхода к синхронизации — через примитивы sync и через каналы. Первые дают низкоуровневый контроль, вторые — более декларативный способ координации.

🐸 Библиотека Go для собеса
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1