Решайте задачи на Go0 из 45 задач решено
Перейти к задачам
1324 просмотра
От 4 июня

Ответы на вопросы с собеседований по Golang

1

Какие типы данных используются в Go?

Go работает со следующими типами: - Method (метод); - Boolean (логический тип); - Numeric (численный); - String (строковый); - Array (массив); - Slice (срез); - Struct (структура); - Pointer (указатель); - Function (функция); - Interface (интерфейс); - Map (карта); - Channel (канал).

2

Как проверить тип переменной в среде выполнения?

Лучшим способом проверки типа переменной во время выполнения является Type Switch. Он анализирует переменные по типу, а не значению. Например: package main import "fmt" 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) } Также можно это сделать с помощью рефлексии: package main import ( "fmt" "reflect" ) func main() { x := 42 fmt.Println("Тип переменной x:", reflect.TypeOf(x)) }

3

Что такое рефлексия в Go?

Рефлексия в Go реализована в пакете reflect и представляет собой механизм, позволяющий коду исследовать значения, типы и структуры во время выполнения, без заранее известной информации о них. Рефлексия полезна в ситуациях, когда нам нужно работать с данными неизвестного типа, например, при сериализации/десериализации данных, реализации ORM систем и так далее. С помощью рефлексии мы можем, например, определить тип переменной, прочитать и изменить её значения, вызвать методы динамически. Это делает код более гибким, но следует использовать рефлексию осторожно, так как она может привести к сложному и трудночитаемому коду, а также снизить производительность. Простые примеры: Определение типа переменной: package main import ( "fmt" "reflect" ) func main() { x := 42 fmt.Println("Тип переменной x:", reflect.TypeOf(x)) } В примере мы используем функцию reflect.TypeOf(), чтобы определить тип переменной x. Программа выведет int, так как x — целое число. Чтение и изменение значений: package main import ( "fmt" "reflect" ) func main() { x := 42 v := reflect.ValueOf(&x).Elem() // Получаем reflect.Value fmt.Println("Исходное значение x:", x) v.SetInt(43) // Изменяем значение x fmt.Println("Новое значение x:", x) } Здесь мы используем reflect.ValueOf() для получения reflect.Value переменной x, а затем изменяем её значение с помощью SetInt(). Динамический вызов методов: package main import ( "fmt" "reflect" ) type MyStruct struct { Field int } func (m *MyStruct) UpdateField(val int) { m.Field = val } func main() { x := MyStruct{Field: 10} // Получаем reflect.Value структуры v := reflect.ValueOf(&x) // Получаем метод по имени method := v.MethodByName("UpdateField") // Вызываем метод с аргументами method.Call([]reflect.Value{reflect.ValueOf(20)}) fmt.Println("Обновленное значение поля:", x.Field) } В этом примере мы создаем экземпляр структуры MyStruct, получаем метод UpdateField с помощью MethodByName и вызываем его динамически с помощью Call. Метод обновляет значение поля структуры.

4

Является ли Go объектно-ориентированным языком?

Да и нет. Хотя в Go есть типы и методы, и он допускает объектно-ориентированный стиль программирования, в нем нет иерархии типов. Концепция «интерфейс» в Go предоставляет другой подход, считающийся простым в использовании и в некотором роде более общим. Есть также способы встраивать типы в другие типы, чтобы обеспечить нечто подобное, но не идентичное подклассам. Более того, методы в Go более общие, чем в C++ или Java: они могут быть определены для любого типа данных, даже для встроенных, таких как простые "unboxed" (представляющие значения) целые числа. Они не ограничиваются структурами (классами). Кроме того, отсутствие иерархии типов делает "объекты" в Go намного легче, чем в таких языках, как C++ или Java.

5

Как в Go реализовано наследование?

Как такового наследования в Go нет, но при этом у нас есть структуры - это специальные типы, в которые мы можем включать другие типы, в том числе такие же структуры. Можно сказать, что Go предпочитает наследованию композицию.

6

Как в Go реализована инкапсуляция?

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

7

Как в Go реализован полиморфизм?

Полиморфизм в Go реализован с помощью интерфейсов. Основная идея заключается в том, что мы можем объявить интерфейсы (контракты на определённое поведение) для наших типов. При этом, для типов мы должны реализовать методы, удовлетворяющие этим интерфейсам. Таким образом, мы сможем работать со всем набором типов, у которых реализовали интерфейсы, как с единым интерфейсным типом.

8

Какие механизмы синхронизации доступны в Go?

В Go примитивы синхронизации — это инструменты из пакета sync (и не только), которые помогают нам гарантировать, что множество горутин может безопасно взаимодействовать с общими данными или координировать свою работу. sync.Mutex: основной примитив блокировки для исключения одновременного доступа к данным. Мьютексы позволяют только одной горутине получить доступ к общему ресурсу в определенный момент времени. sync.RWMutex: разрешает множественное чтение или одну операцию записи в текущий момент времени. sync.WaitGroup: используется для ожидания завершения группы горутин перед продолжением выполнения основной программы. sync.Once: гарантирует, что функция будет вызвана только один раз, несмотря на количество вызовов. sync.Cond: предоставляет механизм для блокирования горутины, пока не будет выполнено некоторое условие. Не так давно Расс Кокс отменил предложение удалить данные тип в будущей версии Go. Подобную роль играют: Каналы. Каналы в Go хоть и не являются примитивами синхронизации в традиционном понимании, они играют ключевую роль в управлении горутинами, позволяют обеспечить безопасный обмен данными между ними. Каналы обеспечивают синхронизацию и блокируют выполнение до тех пор, пока данные не будут переданы или приняты. Атомарные операции: Golang предоставляет атомарные операции для безопасного выполнения операций чтения и записи разделяемых данных.

9

Что такое атомарная операция?

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

10

Что такое горутина? Как ее остановить?

Горутина  —  это функция или метод, которые выполняются конкурентно с любыми другими горутинами, используя специальный поток. Потоки горутин более легковесны, чем стандартные потоки, и большинство программ Go одновременно используют тысячи горутин. Для создания горутины перед объявлением функции нужно добавить ключевое слово go. go f(x, y, z) Остановить горутину можно отправкой сигнала в специальный канал. При этом горутины могут отвечать на такие сигналы, только если им сказано выполнять проверку. Поэтому нужно будет включить проверки в подходящие места, например в начало цикла for. package main func main() { quit := make(chan bool) go func() { for { select { case <-quit: return default: // … } } }() // … quit <- true }

11

Что такое канал и какие виды каналов есть в Go?

Каналы — это инструменты коммуникации между горутинами. Технически это конвейер/труба, откуда можно считывать или помещать данные. То есть одна горутина может отправить данные в канал, а другая — считать помещенные в этот канал данные. Для создания канала в Go есть ключевое слово chan. Канал может передавать данные только одного типа. package main import "fmt" func main() { var c chan int fmt.Println(c) } При простом определении переменной канала она имеет значение nil, то есть по сути канал неинициализирован. Для инициализации применяется функция make(). В зависимости от определения емкости канала он может быть буферизированным или небуферизированным. Для создания небуферизированного канала вызывается функция make() без указания емкости канала: var intCh chan int = make(chan int) Буферизированные каналы также создаются с помощью функции make(), только в качестве второго аргумента в функцию передается емкость канала. Если канал пуст, то получатель ждет, пока в канале появится хотя бы один элемент. chanBuf := make(chan bool, 3) С каналом можно произвести 4 действия: - создать канал - записать данные в канал - вычесть что-то из канала - закрыть канал Однонаправленные каналы: в Go можно определить канал, как доступный только для отправки данных или только для получения данных. Канал может быть возвращаемым значением функции. Однако, следует внимательно подходить к операциям записи и чтения в возвращаемом канале.

12

Как работают буферизованные и небуферизованные каналы?

Буферизованные каналы позволяют вам быстро помещать задания в очередь, чтобы вы могли работать с большим количеством запросов и обрабатывать их позже. Кроме того, буферизованные каналы можно использовать в качестве семафоров, ограничивая пропускную способность вашего приложения. Суть: все входящие запросы перенаправляются на канал, который обрабатывает их по очереди. Завершая обработку запроса, канал отправляет исходному, вызвавшему сообщение о готовности обработать новый запрос. Таким образом, ёмкость буфера канала ограничивает количество одновременных запросов, которые он может хранить. Вот так выглядит код, который реализует данный метод: package main import ( "fmt" ) func main() { numbers := make(chan int, 5) // канал numbers не может хранить более пяти целых чисел — это буферный канал с емкостью 5 counter := 10 for i := 0; i < counter; i++ { select { // здесь происходит обработка case numbers <- i * i: fmt.Println("About to process", i) default: fmt.Print("No space for ", i, " ") } // мы начинаем помещать данные в numbers, однако когда канал заполнен, он перестанет сохранять данные и будет выполняться ветка default } fmt.Println() for { select { case num := <-numbers: fmt.Print("*", num, " ") default: fmt.Println("Nothing left to read!") return } } } Аналогично, мы пытаемся считывать данные из numbers, используя цикл for. Когда все данные из канала считаны, выполнится ветка default и программа завершится с помощью оператора return. При выполнении кода выше мы получаем такой вывод: $ go run bufChannel.go About to process 0 . . . About to process 4 No space for 5 No space for 6 No space for 7 No space for 8 No space for 9 *0 *1 *4 *9 *16 Nothing left to read! В общем: Буферизированный канал заблокирует горутину только в том случае, если весь буфер забит. И происходит попытка еще одной записи. Как только будет выполнено чтение из канала - горутина разблокируется. В случае, если горутина всего одна (только функция main) и канал её заблокирует — программа выпадет с ошибкой, так как все горутины блокированы и выполнять нечего. Небуферизированный канал заблокирует горутину до момента, пока с него ничего не прочитают.

13

Можно ли в 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. Это безопасный способ обеспечить корректное управление жизненным циклом канала.

Логотип ДевстанцииАвторизуйтесь, чтобы просматривать следующий контент
15

Что такое пакеты в Go?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
16

Как работает управление памятью в Go?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
17

Что такое глобальная переменная?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
18

Что такое heap и stack?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
19

Что делает runtime.newobject()?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
20

Что такое {} с необъявленным оператором в Go функции?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
21

Как выполнить ряд условий в одном операторе switch case?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
22

Что такое строки в Go?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
23

Как можно оперировать строками?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
24

Как узнать длину строки?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
25

Какие численные типы есть в Go?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
26

Расскажите про числовые константы в Go

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
27

Чем отличается int от uint?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
28

Что такое обычный int и какие есть нюансы его реализации?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
29

Как преобразовать строку в int и наоборот?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
30

Сколько в памяти занимают int32 и int64?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
31

Какие предельные значения int32 и int64?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
32

Какой результат у деления int на 0 и float на 0?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
33

Что такое iota?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
34

Что такое слайс и чем он отличается от массива?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
35

Как работает базовая функция append для Go?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
36

Какой размер массива выделяется под слайс при его расширении за рамки емкости?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
37

Как реализована map (карта) в Go?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
38

Почему нельзя брать ссылку на значение, хранящееся по ключу в map?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
39

Что такое эвакуация, и когда она происходит?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
40

Какие есть особенности синтаксиса получения и записи значений в map?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
41

Как происходит поиск по ключу в map?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
42

Что такое интерфейсы в Go?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
43

Приведите пример реализации интерфейсов

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
44

Что такое пустой интерфейс?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
45

Что такое nil интерфейс?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
46

Как преобразовать интерфейс к другому типу?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
47

Как определить тип интерфейса?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
48

Зачем используется ключевое слово defer в Go?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
49

Порядок возврата при использовании несколько функций с defer?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
50

Как передаются значения в функции, перед которыми указано defer?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
51

Что такое замыкания функций?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
52

Что такое lock-free структуры данных?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
53

Как устроен сетевой ввод-вывод в Go?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
54

Что такое дженерики (обобщения)?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
55

Для чего нужна функция recover()?

Логотип ДевстанцииАвторизуйтесь, чтобы получить доступ
Хотите стать частью сообщества Девстанции?
Вступайте в наш чат в Telegram

Также в этой категории

Задачник
  21 задача

Топ 20 задач с собеседований по Go

Алгоритмические задачи для собеседований Go-разработчиков

4917 просмотров
От 25 февраля

Вам может быть интересно

Задачник
  90 задач

Топ 90 алгоритмических задач по Python

Подборка решений для задач с собеседований Python-разработчиков

8953 просмотра
От 27 июня
Вопросник
  32 вопроса

30 вопросов с собеседований по Python

Ответы на вопросы с собеседований по Python

1192 просмотра
От 4 июня
Вопросник
  60 вопросов

Топ 60 вопросов про базы данных и SQL

Ответы на вопросы с собеседований по БД и SQL

878 просмотров
От 4 июня
Викторина
  12 вопросов

Викторина по Python - Middle/Senior

Продвинутые вопросы для собеседования Python-разработчика

64 просмотра
От 30 мая 2023
Вопросник
  11 вопросов

Теория шардинга баз данных

О распределении данных между серверами

556 просмотров
От 10 октября 2023
Викторина
  20 вопросов

Викторина по Django - Junior/Middle

Базовые вопросы с собеседования по Django

40 просмотров
От 2 июня 2023

Топ тредов

: Предложите идею и получите спонсорский доступ на месяц

Последнее сообщение:
: Ой, не увидела. Круто, спасибо!
8 сообщений
184 просмотра

: Можно добавить таймер на решение задач

Последнее сообщение:
: да, с момента открытия и до успешных тестов. Чтобы трекать время выполнения )
3 сообщения
114 просмотров

Gravatar for 253malvina
Malvina
: Добавьте angular раздел

Последнее сообщение:
: Раздел открыт!
3 сообщения
135 просмотров

Все категории