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

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

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

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

Наши каналы: https://t.me/proglibrary/9197
Download Telegram
🧑‍💻 Статьи для IT: как объяснять и распространять значимые идеи

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

Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.

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

👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
💬 Как ведут себя срезы в Go на граничных значениях?

🔸 Создание среза: срез может быть создан с использованием выражения a[low : high], где a — массив или другой срез, low — начальный индекс, а high — конечный индекс (не включительно). Если low равно 0, его можно опустить. Если high равно длине массива, его также можно опустить.

🔸 Границы индексов: значения low и high должны удовлетворять условиям 0 <= low <= high <= cap(a), где cap(a) — это емкость исходного массива или среза. Попытка использовать индексы за пределами этих границ приведёт к панике.

🔸 Пустые срезы: если low и high равны, срез будет пустым, но валидным. Например, a[2:2] создаст пустой срез.

🔸 Выход за границы: если low или high выходят за границы допустимых значений, компилятор выдаст панику. Например, если len(a) равно 5, то a[0:6] вызовет панику, так как 6 превышает допустимую границу.

🔸 Изменение исходного массива: срезы в Go являются ссылками на исходный массив. Это означает, что изменения в срезе отразятся на исходном массиве и на всех других срезах, сделанных из этого массива.

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

🔸 Увеличение емкости среза: если при добавлении элементов в срез его емкость оказывается недостаточной, Go автоматически создаст новый массив с большей емкостью и скопирует в него элементы из исходного среза.
👍126🔥3
💬 Можно ли в Go закрыть канал со стороны читателя?

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

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

📌 Простой пример:

func main() {
dataCh := make(chan int)
stopCh := make(chan struct{})

go func() {
for {
select {
case data, ok := <-dataCh:
if !ok {
// Канал закрыт, прекращаем обработку
return
}
// Обработка данных
fmt.Println(data)
case <-stopCh:
// Получен сигнал остановки, закрываем канал dataCh
close(dataCh)
return
}
}
}()

// Отправка данных в канал
dataCh <- 1
dataCh <- 2

// Отправка сигнала остановки
stopCh <- struct{}{}
}


stopCh используется для уведомления горутины о необходимости закрыть канал dataCh. Это безопасный способ обеспечить корректное управление жизненным циклом канала.
👍191
💬 Как устроен сетевой ввод-вывод в Go?

Сетевой ввод-вывод в Go организован через пакет net стандартной библиотеки, который предоставляет обширный API для работы с сетью. Он использует модель неблокирующего ввода-вывода с горутинами для обеспечения масштабируемости и эффективности.

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

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

📌 Простой пример:

package main

import (
"fmt"
"io"
"net"
"os"
)

func main() {
// Слушаем на порту 8080
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("Ошибка при создании слушателя:", err)
os.Exit(1)
}
defer listener.Close()
fmt.Println("Сервер запущен и слушает на порту 8080")

for {
// Принимаем входящее подключение
conn, err := listener.Accept()
if err != nil {
fmt.Println("Ошибка при принятии подключения:", err)
continue
}

// Обработка подключения в отдельной горутине
go handleConnection(conn)
}
}

// handleConnection обрабатывает отдельное подключение
func handleConnection(conn net.Conn) {
defer conn.Close()
fmt.Println("Подключился клиент:", conn.RemoteAddr().String())

// Отправляем сообщение клиенту
_, err := io.WriteString(conn, "Привет от сервера!\n")
if err != nil {
fmt.Println("Ошибка при отправке сообщения:", err)
return
}

fmt.Println("Сообщение отправлено клиенту:", conn.RemoteAddr().String())
}
👍141
💬 Что такое table-driven тесты и как их реализовать в Go?

Table-driven тесты в Go — это метод написания тестов, при котором тестовые кейсы организованы в виде таблицы данных.

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

📌 Для реализации table-driven тестов в Go обычно используется следующий шаблон:

1. Определяем структуру, которая описывает тестовый кейс, включая входные данные и ожидаемый результат.
2. Создаем срез этих структур, где каждый элемент представляет отдельный тестовый кейс.
3. Используем цикл for для итерации по срезу тестовых кейсов.
4. Внутри цикла вызываем функцию, которую тестируем, и сравниваем результат с ожидаемым значением.

📌 Пример:

package mypackage

import "testing"

func TestMyFunction(t *testing.T) {
cases := []struct {
name string
input int
want int
}{
{"case1", 1, 2},
{"case2", 2, 4},
// …
}

for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
got := MyFunction(c.input)
if got != c.want {
t.Errorf("MyFunction(%d) == %d, want %d", c.input, got, c.want)
}
})
}
}


MyFunction
— это функция, которую мы тестируем. Для каждого тестового кейса в срезе cases мы запускаем тест, используя t.Run, что также обеспечивает хорошую организацию вывода тестов и их независимость.
12👍8🥱1
-35% на курс по алгоритмам

🎄 Новый год начинается с подарков, а хороший подарок для себя — новые знания со скидкой 35%!

🌟«Алгоритмы и структуры данных» — 23 390 ₽ (вместо 35 990 ₽)

Полугодовая программа от преподавателей МФТИ и НИУ ВШЭ, которая включает в себя все необходимые знания по алгоритмам для работы.

Самое ценное — это развернутая обратная связь по всем вашим домашним заданиям, а также ссылки на полезные дополнительные материалы.

У вас не будет шансов не усвоить какие-то темы курса👌

🔥 Переходите и активируйте вводные занятия курсаhttps://proglib.io/w/ff97c30a
😁3🤔1
💬 Что такое Minimal Version Selection в контексте Go?

Minimal Version Selection (MVS) в Go — это алгоритм, используемый системой управления зависимостями модулей Go (введённый в Go 1.11 с появлением поддержки модулей).

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

📌 Основные аспекты MVS:

1. Минимальная версия: MVS выбирает минимально возможную версию каждой зависимости, которая удовлетворяет всем требованиям версий, указанным в зависимостях проекта и его модулях. Это означает, что если ваш модуль зависит от модуля A версии 1.2 и модуля B, который в свою очередь зависит от модуля A, но версии 1.1, MVS выберет версию 1.2 модуля A, так как это минимальная версия, удовлетворяющая обоим требованиям.

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

3. Уменьшение риска несовместимости: поскольку MVS выбирает минимальную версию, это помогает избежать непреднамеренного обновления до более новых, потенциально несовместимых версий зависимостей.

4. Файл go.mod: все зависимости и их версии явно указываются в файле go.mod проекта. MVS использует этот файл для определения, какие версии зависимостей использовать.

5. Простота обновления: если нам требуется обновить зависимость до более новой версии, достаточно обновить эту версию в файле go.mod. MVS автоматически учтёт это изменение при следующей сборке.

👉 Подробнее
👍9
💬 Что важно помнить при использовании мапы типа any?

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

📌 Возьмем простой пример:

b := getMessage()
var m map[string]any
err := json.Unmarshal(b, &m)
if err != nil {
return err
}


Добавим следующий JSON:

{
"id": 32,
"name": "foo"
}


Поскольку мы используем общую мапу map[string]any, она автоматически парсит все поля: map[id:32 name:foo]

При использовании мапы типа any важно помнить о том, что любое числовое значение, независимо от того, содержит оно десятичное число или нет, преобразуется в тип float64.

Выведем тип m["id"] и убедимся в этом:

fmt.Printf("%T\n", m["id"])

float64


Важно не делать ошибочных предположений и не ожидать, что числовые значения без десятичных знаков будут по умолчанию преобразованы в целые числа.
💯174
💬 Что из себя представляют числовые константы в Go?

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

Они принимают свой тип (например, int, float64) только когда это необходимо, например, при присваивании значения переменной или при использовании в операции, где требуется определённый тип. Это дает гибкость и предотвращает потерю информации из-за ограничений размера типа, особенно при выполнении математических операций с константами.

📌 Простой пример:

package main

import "fmt"

const (
Big = 1 << 100
Small = Big >> 99
)

func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 { return x * 0.1 }

func main() {
fmt.Println(needInt(Small))
fmt.Println(needFloat(Small))
fmt.Println(needFloat(Big))
}
👍6
💬 В чем преимущества и недостатки использования ORM по сравнению с использованием встроенных возможностей Go для работы с SQL?

📌 Преимущества ORM:

1. Удобство и скорость разработки: ORM позволяет взаимодействовать с базой данных, используя объектно-ориентированный подход, что часто упрощает и ускоряет процесс разработки.

2. Безопасность: ORM может помочь избежать некоторых распространенных уязвимостей за счет использования встроенных механизмов защиты.

3. Независимость от базы данных: ORM обеспечивает абстракцию, которая позволяет легче переходить между различными СУБД, не изменяя большую часть кода приложения.

4. Упрощение рефакторинга и поддержки: поскольку логика доступа к данным централизована, вносить изменения и поддерживать приложение становится проще.

📌 Недостатки ORM:

1. Производительность: ORM может быть менее эффективным по сравнению с оптимизированными вручную SQL-запросами, особенно в сложных сценариях.

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

3. Ограничения: некоторые ORM могут ограничивать способность разработчика использовать все функции и возможности конкретной СУБД.

4. Кривая обучения: для эффективного использования ORM требуется время на изучение его особенностей и лучших практик.

📌 Примеры ORM для Go: gorm, Beego ORM, SQLBoiler и другие.
👍7
💬 Почему встраивание в Go не является наследованием, как в классическом ООП?

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

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

В Go, это скорее способ композиции, чем наследования. Встраиваемая структура ничего не знает о том, где она используется, и не может переопределить методы структуры, в которую она встроена.
👍11
🏃 Самоучитель по Go для начинающих. Часть 5. Условные конструкции if-else и switch-case. Цикл for. Вложенные и бесконечные циклы

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

👉 Читать статью
👉 Часть 1
👉 Часть 2
👉 Часть 3
👉 Часть 4
👍6
💬 В каких случаях в Go могут возникнуть deadlocks?

📌 Причины возникновения дедлоков в Go:

1. Горутины, ожидающие друг друга: горутины могут входить в состояние дедлока, если они ожидают ресурсы или сигналы друг от друга, образуя циклическую зависимость.
2. Неправильное использование каналов: попытка чтения из закрытого канала или блокировка на отправке/получении данных из-за отсутствия получателей/отправителей, может привести к дедлоку.
3. Злоупотребление блокировками: использование мьютексов и других примитивов синхронизации без должной осторожности может вызвать дедлоки. Например, попытка захватить мьютекс, который уже захвачен текущей горутиной, приведет к блокировке.
👍169
💬 Можно ли в функциях Go использовать необязательные аргументы?

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

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

Например, можно создать функцию funcStructOpts(opts Opts), которая принимает структуру Opts, и вызвать её как funcStructOpts(Opts{p1: 1, p2: 2, p8: 8, p9: 9, p10: 10}), где Opts — структура с полями p1, p2, p8, p9, p10 и так далее.

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

Например, можно определить функцию funcWithOpts(opts ...Option), и вызвать её как funcWithOpts(WithP1(1), WithP2(2), WithP8(8), WithP9(9), WithP10(10)), где WithP1, WithP2, WithP8, WithP9, WithP10 являются функциями, возвращающими тип Option, который может быть функцией, изменяющей внутреннее состояние вызываемой функции funcWithOpts.
👍108
Самые полезные каналы для программистов в одной подборке!

Сохраняйте себе, чтобы не потерять 💾

🔥Для всех

Библиотека программиста — новости, статьи, досуг, фундаментальные темы
Книги для программистов
IT-мемы
Proglib Academy — тут мы рассказываем про обучение и курсы

#️⃣C#

Библиотека шарписта
Библиотека задач по C# — код, квизы и тесты
Библиотека собеса по C# — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Вакансии по C#, .NET, Unity Вакансии по PHP, Symfony, Laravel

☁️DevOps

Библиотека devops’а
Вакансии по DevOps & SRE
Библиотека задач по DevOps — код, квизы и тесты
Библиотека собеса по DevOps — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования

🐘PHP

Библиотека пхпшника
Вакансии по PHP, Symfony, Laravel
Библиотека PHP для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по PHP — код, квизы и тесты

🐍Python

Библиотека питониста
Вакансии по питону, Django, Flask
Библиотека Python для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по Python — код, квизы и тесты

Java

Библиотека джависта — полезные статьи по Java, новости и обучающие материалы
Библиотека Java для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по Java — код, квизы и тесты
Вакансии для java-разработчиков

👾Data Science

Библиотека Data Science — полезные статьи, новости и обучающие материалы
Библиотека Data Science для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по Data Science — код, квизы и тесты
Вакансии по Data Science, анализу данных, аналитике, искусственному интеллекту

🦫Go

Библиотека Go разработчика — полезные статьи, новости и обучающие материалы по Go
Библиотека Go для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по Go — код, квизы и тесты
Вакансии по Go

🧠C++

Библиотека C/C++ разработчика — полезные статьи, новости и обучающие материалы по C++
Библиотека C++ для собеса — тренируемся отвечать на каверзные вопросы во время интервью и технического собеседования
Библиотека задач по C++ — код, квизы и тесты
Вакансии по C++

💻Другие профильные каналы

Библиотека фронтендера
Библиотека мобильного разработчика
Библиотека хакера
Библиотека тестировщика

💼Каналы с вакансиями

Вакансии по фронтенду, джаваскрипт, React, Angular, Vue
Вакансии для мобильных разработчиков
Вакансии по QA тестированию
InfoSec Jobs — вакансии по информационной безопасности

📁Чтобы добавить папку с нашими каналами, нажмите 👉сюда👈

🤖Также у нас есть боты:
Бот с IT-вакансиями
Бот с мероприятиями в сфере IT

Мы в других соцсетях:
🔸VK
🔸YouTube
🔸Дзен
🔸Facebook *
🔸Instagram *

* Организация Meta запрещена на территории РФ
👍21
💬 Что такое псевдоним типа (type alias) в Go?

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

Псевдонимы типов вводятся с использованием ключевого слова type, за которым следует новое имя типа, знак равенства и существующий тип. Например, type Bytes = []byte. В этом случае Bytes является псевдонимом для типа []byte. Псевдонимы типов полностью идентичны их оригинальным типам, включая методы, связанные с типом.

📌 Пример:

package main

import "fmt"

type ByteSlice = []byte

func printBytes(slice ByteSlice) {
for _, b := range slice {
fmt.Printf("%x ", b)
}
fmt.Println()
}

func main() {
data := ByteSlice{0x1, 0x2, 0x3}
printBytes(data) // Выведет: 1 2 3
}
👍115
💬 Что такое захват переменной в Go?

Захват переменной в Go происходит в случаях, когда функция или другая конструкция использует переменные, объявленные за пределами своей собственной области видимости (скоупа).

Во вложенном скоупе есть возможность обращаться к переменным, объявленным в скоупе выше (но не наоборот). Обращение к переменным из вышестоящего скоупа — и есть их захват. Например:

func main() {
var funcs []func()

for i := 0; i < 3; i++ {
funcs = append(funcs, func() {
fmt.Println(i)
})
}

for _, f := range funcs {
f()
}
}


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

Чтобы избежать этой проблемы и захватить текущее значение переменной внутри цикла, можно использовать локальную переменную внутри цикла:

func main() {
var funcs []func()

for i := 0; i < 3; i++ {
i := i // создаем локальную переменную
funcs = append(funcs, func() {
fmt.Println(i)
})
}

for _, f := range funcs {
f()
}
}

Результат:
0
1
2
👍12🔥3
🧑‍💻 Статьи для IT: как объяснять и распространять значимые идеи

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

Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.

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

👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
👍2
💬 Для каких целей в Go предназначены теги сборки (build tags)?

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

📌 Ключевые принципы:

1. Код, специфичный для платформы: например, наше приложение должно выполнять разные функции в зависимости от того, работает ли оно на Windows, Linux или macOS. В таком случае мы можем использовать теги сборки для включения соответствующего кода только для конкретной платформы. Например, файл config_windows.go может содержать конфигурации, специфичные для Windows, и будет иметь в первой строке комментарий // +build windows.

2. Экспериментальные фичи: теги сборки также могут использоваться для включения экспериментальных или еще не готовых фич. Например, если у нас есть экспериментальная функция, мы можем пометить её файл с помощью // +build experimental и компилировать приложение с этим тегом только тогда, когда захотим её включить.

3. Кастомные конфигурации: теги сборки позволяют создавать различные конфигурации нашего приложения. Например, мы можем использовать теги для сборки lite-версии приложения с ограниченным функционалом.
🔥7👍2
💬 Можно ли в Go использовать оператор switch без условия?

Да, в Go можно использовать оператор switch без явного условия. В таком случае, switch оценивает каждый case как логическое выражение. Это позволяет создавать более чистый и читаемый код, когда нужен простой способ написания длинных цепочек в духе if-then-else. Например:

x := 42

switch {
case x > 100:
fmt.Println("x is very big")
case x > 10:
fmt.Println("x is big")
default:
fmt.Println("x is small")
}


Здесь switch последовательно проверяет каждое условие и выполняет код в блоке case, который соответствует первому истинному условию.
👍17🥰3🥱1