ru
Feedback
Библиотека Go для собеса | вопросы с собеседований

Библиотека Go для собеса | вопросы с собеседований

Открыть в Telegram

Вопросы с собеседований по Go и ответы на них. Учиться у нас: https://proglib.io/w/0b524a15 По рекламе: @proglib_adv Для обратной связи: @proglibrary_feeedback_bot Наши каналы: https://t.me/proglibrary/9197

Больше
7 425
Подписчики
-524 часа
-77 дней
+630 день
Архив постов
11 сентября | 19:00 Офлайн в Москве | Онлайн Кто выступит: 🔹Виталий Левченко, Engineering manager Wildberries проведет воркш
11 сентября | 19:00 Офлайн в Москве | Онлайн Кто выступит: 🔹Виталий Левченко, Engineering manager Wildberries проведет воркшоп на тему LLM для конкурентного Go кода; 🔹Владимир Марунин, Senior Developer Clatch, МТС Web Services расскажет про эффективное использование sync.Map в Go; 🔹Роман Ерема, Developer MWS Cloud Platform поделится опытом разработки Cloud Controller Manager: интеграции Kubernetes с облаком MWS. Для участия зарегистрируйся по ссылке. До встречи на True Tech Go!

Для чего нужен пакет cgo и что он собой представляет Cgo создаёт мост между Go и C: код на Go вызывает функции C через пространство имён C, а код на C может вызывать экспортированные функции Go. Cвойства cgo: Встраивание кода C в преамбуле перед import "C". Доступ к именам из заголовков и преамбулы через пространство имён C. Преобразования типов выполняются явно при переходе из C.* к чистым типам Go и обратно. Память: объекты, выделенные в C, освобождаются C.free, длительное хранение указателей на Go-память в C запрещено.
package randc

/*
#include <stdlib.h>
*/
import "C"

func Random() int {
  return int(C.random()) // C.long -> Go int
}

func Seed(i uint) {
  C.srandom(C.uint(i)) // Go uint -> C unsigned int
}
Разбор примера: import "C" псевдопакет: ссылка на пространство имён C. В rand четыре обращения к C: вызовы C.random и C.srandom, приведение C.uint(i) и сам импорт. Random вызывает random из libc. В C она возвращает long (C.long в cgo), результат приводится к Go int перед выдачей наружу. Seed принимает Go int, приводит к C.uint и передаёт в srandom. Комментарий непосредственно перед import "C"преамбула. Она компилируется как C-заголовок для C-частей пакета: здесь можно объявлять/определять функции и переменные, затем использовать их из Go через C.*. 🐸 Библиотека Go для собеса

Тест для Golang-разработчиков, проверьте свои знания, готовы ли вы к обучению на курсе. 💻 Ответьте на 20 вопросов за 30 мину
Тест для Golang-разработчиков, проверьте свои знания, готовы ли вы к обучению на курсе. 💻 Ответьте на 20 вопросов за 30 минут и проверьте, готовы ли вы к обучению на онлайн-курсе «Golang Developer. Professional» от OTUS. Сейчас Go становится все востребованнее, благодаря своей производительности, масштабируемости и экосистеме. После 5 месяцев обучения вы сможете: — Писать production-ready код, многопоточные и конкурентные программы. — Понимать синтаксис и внутреннее устройство языка Go. — Разворачивать микросервисы с помощью Docker. — Проектировать и реализовывать микросервисную архитектуру на Go. Также вас ждет прокачка навыков на реальных коммерческих кейсах и под руководством экспертов в этой области. Старт курса 30 июля, успейте на курс .Возможна рассрочка. 👉 ПРОЙТИ ТЕСТ Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru

⚡️ Бесплатный вебинар — прогнозируем цены и не сходим с ума 21 августа в 19:00 МСК будет бесплатный вебинар с Марией Жаровой — экспертом в ML и Data Science. Тема:
«Введение в машинное обучение: как спрогнозировать стоимость недвижимости».
Подробности рассказываю в гс выше — включай, чтобы не пропустить.

Как осуществить своп двух переменных без временной переменной 1️⃣ Сложение и вычитание (без временной переменной)
a := 5
b := 3

a = a + b // a становится 8
b = a - b // b становится 5 
a = a - b // a становится 3
a и b меняются местами за три операции без использования временной переменной. 2️⃣ XOR (исключающее ИЛИ)
a := 5 // 0101 в двоичной системе
b := 3 // 0011 в двоичной системе

a = a ^ b // a становится 6 (0110)
b = a ^ b // b становится 5 (0101)
a = a ^ b // a становится 3 (0011)
Двойное применение XOR с теми же операндами восстанавливает исходное значение, поэтому значения переставляются. 3️⃣ Простая функция со свопом
func main() {
    fmt.Println(swap())
}

func swap() []int {
    a, b := 15, 10
    b, a = a, b
    return []int{a, b}
}
swap меняет a и b местами и возвращает результат. 🐸 Библиотека Go для собеса

🔥 Последняя неделя, чтобы забрать курс по AI-агентам по старой цене! Пока вы тестируете Copilot, другие уже учатся строить A
🔥 Последняя неделя, чтобы забрать курс по AI-агентам по старой цене! Пока вы тестируете Copilot, другие уже учатся строить AI-агентов, которые реально работают на бизнес. Хватит отставать! Наш курс — это концентрат практики по LangChain и RAG. Улучшенная версия, доработанная по отзывам первого потока. 📆 Старт — 15 сентября. 💸 Цена 49 000 ₽ — только до 24 августа. 👉 Зафиксировать цену

Опишите принцип работы стека и приведите реализацию на Go Стек — тип данных, основанный на принципе LIFO (last in — first out, «последним пришёл — первым вышел»). Элементы стека связаны линейно, каждый указывает на следующий, доступ к середине отсутствует. Элемент, помещённый в стек последним, извлекается первым. После извлечения он удаляется, и верхним становится предыдущий. Операции: push — добавление элемента, pop — удаление верхнего элемента, peek — возврат верхнего элемента без удаления. Реализация на Go через срез:
type Stack struct {
    items []int
}

func (s *Stack) Push(item int) {
    s.items = append(s.items, item)
}

func (s *Stack) Pop() int {
    if len(s.items) == 0 {
        panic("Stack is empty!")
    }
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item
}

func (s *Stack) Peek() int {
    if len(s.items) == 0 {
        panic("Stack is empty!")
    }
    return s.items[len(s.items)-1]
}

func (s *Stack) IsEmpty() bool {
    return len(s.items) == 0
}

func (s *Stack) Size() int {
    return len(s.items)
}
Мы использовали срез для хранения элементов стека. Метод Push добавляет элемент на верх стека, метод Pop извлекает его, а метод Peek позволяет посмотреть верхний элемент без его извлечения. Также мы использовали проверку стека на наличие элементов. 🐸 Библиотека Go для собеса

Как проверить тип данных переменной в Go во время выполнения программы Оператор switch в Go позволяет проверять тип переменной во время выполнения. Каждый switch включает хотя бы один оператор case, который работает как условие, и блок default, который выполняется, если ни одно из условий не выполнено. Пример, который проверяет, является ли значение интерфейса i типом int или string:
func do(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Double %v is %v\n", v, v*2)
    case string:
        fmt.Printf("%q is %v bytes long\n", v, len(v))
    default:
        fmt.Printf("I don't know type %T!\n", v)
    }
}

func main() {
    do(21)
    do("hello")
    do(true)
}
Результат:
Double 21 is 42
"hello" is 5 bytes long
I don't know type bool!
🐸 Библиотека Go для собеса

Как анонимные функции и замыкания используются в языке Go • Анонимные функции в Go создаются внутри других функций. Это функции без имени, которые можно вызывать или передавать так же, как обычные функции. • В Go есть возможность доступа к состоянию внешней функции из анонимных функций, даже после завершения внешней функции. Это позволяет создавать замыкания. Замыкание — это функция, которая сохраняет доступ к переменным внешней функции после её завершения. Пример с функцией incrementer, которая создает анонимную функцию для увеличения значения переменной i:
func incrementer() func() int {
    i := 0

    return func() int {
        i++
        return i
    }
}
При вызове incrementer создается новая копия переменной i, и возвращенная функция увеличивает её значение. Каждый последующий вызов incrementer создает свою копию переменной i.
func main() {
    increment := incrementer()
    fmt.Println(increment()) // 1
    fmt.Println(increment()) // 2
    fmt.Println(increment()) // 3

    newIncrement := incrementer()
    fmt.Println(newIncrement()) // 1
}
🐸 Библиотека Go для собеса

Для чего в Go применяется пустой идентификатор Пустой идентификатор _ в Go используется как анонимный заполнитель. Он позволяет обращаться к значению, но не связывает его с переменной. 1️⃣ Игнорирование возвращаемых значений функции: если функция возвращает несколько значений, но нужно только одно из них.
value, _ := someFunction()
2️⃣Игнорирование элементов в range: при переборе элементов среза или карты, когда нужен только ключ или только значение.
for k, _ := range myMap {
    fmt.Println(k)
}
   
for _, v := range mySlice {
    fmt.Println(v)
}
3️⃣Импорт пакета: если пакет нужно импортировать только для выполнения его инициализации, без использования его функций или типов.
import _ "image/png"
4️⃣ Игнорирование переменных в множественном присваивании:
_, y := getCoordinates()
5️⃣ Игнорирование ошибок: можно использовать для игнорирования ошибок, но это не рекомендуется.
result, _ := strconv.Atoi("123")
6️⃣ Использование в анонимных структурах: при создании анонимной структуры, когда не нужно имя для одного из полей.
person := struct {
    Name string
    _ int 
}{"Alice", 30}
➡️ Пустой идентификатор полезен, но злоупотреблять им не следует, особенно для игнорирования ошибок. 🐸 Библиотека Go для собеса

🔎 Ищете способы улучшить работу с данными в Go? На открытом вебинаре разберемся, как использовать итераторы для работы с бол
🔎 Ищете способы улучшить работу с данными в Go? На открытом вебинаре разберемся, как использовать итераторы для работы с большими данными. Поймем, что изменилось с Go 1.23 и какие преимущества дают ленивые итераторы. ❗️ Изучите, как заменить стандартные циклы на более эффективные итераторы и оптимизировать свой код. Освойте лучшие практики разработки на Go! Присоединяйтесь к открытому уроку 19 августа в 20:00 МСК: https://clc.to/M-aU0Q Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru

Что такое гонка данных и как она проявляется в языке Go ➡️ Гонки данных — это ошибки, возникающие в конкурентных системах, когда две горутины одновременно обращаются к одной и той же переменной, и хотя бы одно из обращений — запись. Для предотвращения таких ошибок в Go есть примитивы синхронизации. ➡️ Состояние гонки (Race Condition) описывает ситуацию, когда поведение программы зависит от порядка выполнения операций. Гонка данных — это тип состояния гонки. Пример гонки данных, которая может привести к сбоям и повреждению памяти:
func main() {
    c := make(chan bool)
    m := make(map[string]string)
    go func() {
        m["1"] = "a" // Первый конфликтный доступ
        c <- true
    }()
    m["2"] = "b" // Второй конфликтный доступ
    <-c
    for k, v := range m {
        fmt.Println(k, v)
    }
}
➡️ Для диагностики таких ошибок Go предоставляет встроенный детектор гонок данных. Для его использования достаточно добавить флаг -race при запуске команд:
$ go test -race mypkg   
$ go run -race mysrc.go
$ go build -race mycmd 
$ go install -race mypkg
➡️ Переменная окружения GORACE позволяет настроить параметры детектора гонок, например:
$ GORACE="log_path=/tmp/race/report Strip_path_prefix=/my/go/sources/" go test -race
🐸 Библиотека Go для собеса

🚀 Главная ошибка новичка в ML — строить звездолёт вместо велосипеда Многие сразу хотят свою Midjourney, но в итоге получают
🚀 Главная ошибка новичка в ML — строить звездолёт вместо велосипеда Многие сразу хотят свою Midjourney, но в итоге получают только выгорание. Успех начинается с «велосипеда»: научитесь предсказывать цены или классифицировать отзывы. Освойте базу, а уже потом стройте «звездолёты». Наш курс «ML для старта в Data Science» — это и есть тот самый правильный старт от простого к сложному. 👉 Начните правильно Берёте курс «ML для старта» до конца недели — Python в подарок. ❗А 21 августа пройдет бесплатный вебинар с Марией Жаровой: узнаете, какие проекты качают скилл, а какие качают ваши нервы. А какой самый сложный проект вы брались делать в самом начале? 🫢

Что включает в себя набор примитивов синхронизации пакета sync в Go Примитивы синхронизации — инструменты (в основном из пакета sync), которые обеспечивают безопасную работу с общими данными и координацию горутин.sync.Mutex — базовый механизм взаимного исключения. Позволяет заблокировать доступ к ресурсу, чтобы только одна горутина могла его изменять в текущий момент.sync.RWMutex — вариант мьютекса с разделением на блокировку для чтения и записи: Одновременно могут выполняться несколько операций чтения. Запись возможна только при полном исключении доступа.sync.WaitGroup — позволяет дождаться завершения группы горутин. Удобен для синхронного старта/остановки процессов. • sync.Once — гарантирует однократное выполнение кода, даже при множественных вызовах из разных горутин. • sync.Cond — условная переменная, при помощи которой горутина может "заснуть" до наступления определённого события. Go предлагает два подхода к синхронизации — через примитивы sync и через каналы. Первые дают низкоуровневый контроль, вторые — более декларативный способ координации. 🐸 Библиотека Go для собеса

Как минимизировать использование памяти в Go при работе с большими структурами данных ➡️ Избегайте глобальных переменных, они остаются в памяти на весь срок работы программы. ➡️ Выбирайте правильные типы данных используйте int8, int16 вместо int, если диапазон позволяет. ➡️ Используйте sync.Pool для повторного использования часто создаваемых объектов. ➡️ Ленивая инициализация, инициализируйте данные только по мере необходимости. ➡️ Передавайте указатели на структуры, это экономит память по сравнению с копиями. ➡️ Срезы против массивов — используйте массивы, если размер данных известен заранее. ➡️ Освобождение ресурсов, присваивайте nil неиспользуемым структурам. ➡️ Используйте буферизацию, она снижает количество выделений памяти. ➡️ Оптимизируйте структуры, переупорядочив поля, можно снизить их размер. 🐸 Библиотека Go для собеса

Что собой представляет «затенение» переменной (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 для собеса

Расскажите о механизме мьютексов в 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 для собеса

Как 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 для собеса

Чем является 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 для собеса

Как создать простой веб-сервер на 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. ➡️ После запуска кода при переходе по адресу http://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 для собеса