Golang Flashcards

1
Q

What data types exist in Golang?

A

In Golang we have few different data types, which are:
- integer
- float
- boolean
- byte
- string
- array and slice
- structure
- map
- interface
- rune

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

What is difference between:
- type Example = int
- type Example int

A

The difference is that in first case we create type synonym, what means that it is completely equal to original type itself, and can be freely used instead of it with full compatibility
In second case you create new type based on original type. This is separate type and can’t be used instead of original one without explicit typecasting, but you can add custom methods to this new type.
And this limitation, that you can’t freely operate two different.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

What is the difference between regular functions and anonymous functions

A

The main difference is that anonymous functions are closures. In other words, they can freely access variables from environment where they were created.
Second big difference is that using anonymous functions you can create Immediately Invoked Function Expressions.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

What is difference between errors in Golang and exceptions in other languages?

A

Exceptions in other languages is something what you didn’t expect to happen and they are just ignored by default.
But in Golang, errors are quite transparent, the language itself makes you understand possible problems at the moment when you write code, and do something to predict program behavior in bad case and handle this error.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

What is difference between type conversion and type assertion in Golang?

A

The difference between type conversion int(exampleValue) and type assertion exampleValue.(int) is that in first case we transform value of one type into value of another type.
In second case we tell the compiler, that this variable type is equal to that type we assert to.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

When do we need to use function “new” in Golang?

A

I would say, we can use new keyword everywhere where we could need to create a pointer to a value of certain type without explicit initialization

If we just try to write something like: var p *float64 = &3.5 (var p asterisk float64 equals ampersand 3 dot five) then we will get error, because in Golang primitives are provided by value and they don’t have address that you can copy and then use
There are two ways to do this.
In first case, you create separate variable first like var value = 3.5 and then on next row you write var p = &value
Second way, on first row you write var p = new(float64) and on next row you write *p = 3.5

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

What “defer” is used for in Golang?

A

Defer is a statement that you can use to execute code that goes after defer after function execution is completed. And by function completion here I mean not only that all function code was successfully executed, but also include early return and panic cases.

Defer is usually used to close streams or recover from panic. Also, you can use few defer statements in same function. This case first will be executed last called defer statement.

Finally, there are two cases when deferred code won’t be executed. These are cases when function work was finished through calling os.Exit() or log.Fatal() functions.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

What is mutex?
What is difference between sync.Mutex and sync.RWMutex?

A

Mutex is a structure that allows to control access to certain parts of code from multiple goroutines.
Using mutex is neccessary in order to avoid race condition, when few different goroutines are trying to access same variable, for example.

The difference between Mutex and RWMutex is that Mutex always guarantee serial access to protected source, but RWMutex allow parallel access for reading.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

What is pprof?

A

As far as I know, this is Golang profiling tool. It can be used to build charts and graphs, showing function calls and how long it takes for each of them.
It can be also used to test HTTP API endpoints performance.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

What types in Golang are incomparable?

A
  • Slices and maps, except of nil comparison
  • Functions
  • Other structures, containing any of listed above
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

In what cases the following code will fail with error?

a := map[B]int{}
a[d] = 0
e, ok := a[d]

A
  1. If B or d is not defined
  2. If B is not type
  3. a is already defined above
  4. Race condition because of concurrent map writing
  5. Type of d is not B
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Is that possible to avoid race condition when multiple goroutines are trying to write into same resource without use of mutex or other synchronization mechanisms?

A

Yes, if GOMAXPROCS will be set to 1.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

What’s a typical use case for anonymous functions?

A

Callbacks, goroutines creation and immediately invoked function expressions.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

What is interface in Golang? How it works?

A

Interface in golang is an abstraction that describes set of methods that certain type must implement. If certain type implements these methods, then it implements interface too.
Golang is different from most of modern languages. In them you have to explicitly declare that this or that class or type implements this interface. In Golang works the opposite mechanism - if certain type has all required methods, it implicitly implements this or that interface.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

What is difference between goroutines and threads?

A

The difference is in their basis, in what they are and how they work with system resoruces.

Goroutines are lightweight, they are administrated and schelduled by golang runtime in userspace.
Goroutines have some restrictions, for example, they don’t have direct access to system resources, but these limitation make them faster, more effective and effective, and again, more lightweight than regular system threads. Their minimal stack size is like two kilobytes, but it can grow up to one gigabyte if neccessary. Also, because of this lighweightness, you can create almost infinite number of goroutines while you have enough memory. You can have thousands of them.

From the other side, threads are managed by system itself in kernel space. As far as I know, these threads have much more possibilities in system interaction than goroutines, and much larger stack, what, from other side, can’t grow the same way like goroutines can do.
The main problem of threads is that they are heavy and switching between them require full context change, which is quite heavy operation too. So, you can’t create thousands of threads like you can do with goroutines.
Also, if to talk about threads, then their behavior and details can differ from one system to another, what can affect program execution. Goroutines, same time, provide more stable and predictable environment.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

What are channels in Golang?

A

Channels are a mechanism of interaction and data transfer between goroutines.
There are two types of channels - with buffer and without.
Channels without buffer will force two goroutines to synchronize together when sending or reading something using it, because it has no inner data storage it any value put into it must be handled at same moment of time.
Channels with buffer work like a queue, and until they are overflown, you can handle information sent through them asynchronously.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

What is an empty interface in Golang?

A

Empty interface is an interface with no declared methods in its body. Empty interface means “any type”, and if you used empty interface as function parameter type, for example, then that means that you can provide anything as function argument. This can be useful when you want to work with different types, but by some reason don’t want or can’t use generics, or in case if you simply don’t know exact type of value, for example, when you work with JSON with unknown structure.

But if we dive a bit deeper under this high-level of abstraction that I just explained, then the reason why empty interface can be used to declare variable or parameter as any type, is because of interface realization in Golang…
[Начинаю рассказывать про интерфейсы]

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
18
Q

How does Golang garbage collector work?

A

Golang garbage collector work consist of two phases.
During first phase it goes through all objects from the root level recursively, and then marks all objects that it can access through references as alive.
During second phase it deletes all objects, that are not alive.
Golang garbage collector works in parallel to main go program, and prevent program from being entirely paused by it.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
19
Q

How does capacity changes when you append elements into slice?

A

Up to 1024 it doubles each time there is not enough space in slice to add new elements.
After 1024 - by 25%

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
20
Q

Explain how to convert string to number and number to string in Go

A

Using Itoa and Atoi function from strconv package

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
21
Q

How strings work in golang internally?

A

In Golang, strings are non-changeable arrays of bytes.
UTF-8 is used as standart encoding.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
22
Q

What is the difference between slices and arrays?

A

Array is a structure of fixed size, used to store sequence of elements, and what is provided by value.

Slice is a structure provided by reference, that doesn’t store any data itself, but instead uses certain array to store data, and slice itself is just kind of window to this array. Also, slices size can be dynamically changed.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
23
Q

How does map work in Go?

A

Map is a structure, that uses hashtable to store values by their keys.
It has inbuild collision handling mechanism. If two keys produce same hash, collision will be solved through storing two values in chain in table cell.
Its average time complexity is O(1)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
24
Q

What is context?

A

In Golang, context is a structure, that is used to manage operations flow, cancel certain functions execution when needed and share certain data.

Context can be safely shared between few goroutines.
As I already said, context can be cancelled. This functionality is realized through special channel, that closes when context must be cancelled.
Context can be closed manually, or can have certain timeout or dealine, after what it will be closed automatically.

To sum up, contexts are a tool for managing the execution time and cancellation of operations in networked and multithreaded applications. They help build responsive applications by providing ways to control the duration of operations and safely cancel them.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
25
Q

How OOP is implemented in Go?

A

The concepts of object-oriented programming (OOP) are implemented differently than in traditional OOP languages like Java or C++. Go doesn’t use classes, inheritance, or class-based polymorphism same way like other languages do. Instead, it relies on interfaces, structures, and embedding in order to achieve flexibility.

The primary way to organize and encapsulate data in Go is through structures. Structures define data structure inside of an entity, but unlike classes, they don’t include method definitions within themselves. Instead, methods are defined separately and associated with structs using method receivers.

Polymorphism in Go is achieved through interfaces. An interface is a set of method signatures that type should have to be considered as type that is implementing an interface. In Go, types can satisfy interfaces implicitly, without explicit declaration.

Also, instead of inheritance, Golang uses composition.
You can embed one struct within another, allowing the embedded struct to handle part of the work.

26
Q

What are the disadvantages of Go?

A

Well, disadvantages…
I would say, first of all, this is implementation of OOP ideas in Golang. You know, we don’t have classes and inheritance in Golang, unlike in other languages, and similar functionality is implemented through different mechanisms. This is not 100% bad, but this can be at least confusing for people who previously worked with languages what implement OOP through more traditional mechanisms.

If to talk about me, I would say that for me, threre are three big inconveniences. First of all, this is impossibility to have structure constructors and have static methods linked to structures.
Also, I think that Go would be better if inbuilt types like integers, strings, slices etc would have inbuilt methods, like we have in JavaScript, for example. In JavaScript if you want to find index of substring inside string, or map array, you call required method right on string or array. In Golang, you have to use imported packages like strings or use loops.

Well, continue about more general disadvantages. I would notice just two of them, these are strings immutability and lack of generics until version 1.18.

Well, I think that these are all disadvantages I can remember now. What do you think about this? Do you know disadvantages that I forgot to say about?

27
Q

What happens if you try to write in a closed channel?
How to check is channel open or closed?

A

You will get panic.
If you want to check if channel is open or closed, you can use channel read syntax with receiving two values instead of one. This case first value will be read value or default value if channel has no items, and second value is boolean value showing is channel open or closed.

28
Q

What are the main data structures in Go?

A

Slices
Slices are dynamic structures that act like arrays that can grow and shrink. They are built on top of arrays but provide more flexible management of element collections. Slices support operations like adding, removing, and accessing elements.

Arrays
Arrays are fixed-size collections that store elements of a single type. The array size must be known at compile time, making them less flexible than slices. However, they are useful for tasks where the exact number of elements is known, potentially improving performance.

Maps
Maps are data structures that store data in key-value pairs. Keys are unique, and each key is associated with a single value. Maps are used for quick lookups, additions, and deletions of data by key.

Structs
Structs are custom data types that allow grouping data of various types. They are widely used for organizing complex data and support encapsulation.

Channels
Channels are mechanisms for data transfer between goroutines, supporting concurrent and asynchronous execution. Channels allow synchronization and communication between goroutines without traditional synchronization primitives, such as mutexes and condition variables.

Interfaces
Interfaces in Go represent sets of methods that define behavior. Types can implement interfaces by providing method definitions.

29
Q

What are Golang advantages?

A

First advantage is minimalism. This it both advantage and disadvantage. Minimalism means that Golang provides no more than enough do to all tasks it have to do. In Golang, there is only one way to do something, if we are talking about basic things.
For example, if you want to iterate over an array in JavaScript, you can use while loop, do while loop, for loop, for of loop, for in loop, forEach, map and reduce methods. In Golang, you can use for loop. Maybe with range, but anyway, this is much less things than in other languages.
This makes Go easy to learn and read.

Second advantage is performance. Golang performance is comparable with low-level languages like C or C++, but same time, Golang is much more convenient and provides higher level of abstraction, which makes program development in Golang faster.

Third is wide parallelism and asynchronous operations support. In Golang, parallelism is extremely intuitive and easy to use, comparing to other languages.

Finally, Golang is crossplatform language. When you use it, you can focus of functionality, not on the environment.

30
Q

How to stop multiple goroutines?

A

Use select and channel inside it. By default you do work, when need to stop, close channel.

31
Q

How does Golang schelduler work?

A

Golang schelduler consist operates three main elements:
- Goroutines
- Machines (wrapper over system thread)
- Processor (virtual controller, containing local goroutines queue and controlling Machine in order to execute Goroutines. Number of Processors is defined by GOMAXPROCS value)

During Golang runtime, one or few Processors are created, correspondingly with Machines, assigned to them.
After that, Goroutines are distributed between Processors.

If Processor has Goroutines, that are not blocked (channel awaiting, awaiting network requests or system operations), it passes one of them to the Machine to execute it.
If processor has no Goroutines, that are not blocked, and any other Processor has such Goroutines in its queue, it will steal them from this queue.

Also, in case if Goroutine performs any blocking system requests, its execution blocks whole Machine, but Processor is free. In order to avoid Processor locking too, Processor creates new Machine this case, and continues execution of other Goroutines.
After Gorotine that has system lock, can continue its execution, its Machine is deleted, and Goroutine is moved to Global queue.

Global queue is a queue, where are located Goroutines, that were released after system locks or what could not be placed into local queues because of its overflow.
Local queue has size of 256 Goroutines.

Now a couple of words about how Processor performs selection of Goroutine to execute.
Usually, it will try to find next Goroutine in its local queue.
If not found, will try to steal from other Processor queue.
If not found, will check global queue.
Also, every 61 switch between Goroutines, it will go to global queue first, ignoring any other sources of Goroutines.

32
Q

What is the difference between concurrency and parallelism?

A

Concurrency is a situation, when few processes are trying to access same resource.
Parallellism is a situatiion, when few processes work in parallel, each with its own resources.

33
Q

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

A

Пустая структура (0 байт)

34
Q

Где у вас в проекте использовались горутины?

A

Пфф, интересный вопрос…
По коду много где, но чтобы так вспомнить, для чего конкретно…

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

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

35
Q

Есть ли у map capacity?

A

Нет

36
Q

Какие ключи должны быть у map?

A

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

37
Q

Как реализована конкурентная модель в Golang?

A

Конкурентная модель реализована с использованием определённых механизмов языка. Это:
- Горутины, что-то вроде потоков, но крайне лёгких и быстрых
- Каналы, структуры которые используются для передачи данных между горутинами
- Определённые конструкции языка, которые используются для работы с горутинами, например select, для реакции на переданные через горутины данные
- Также планировщик Golang поддерживает асинхронность и параллельность выполнения задач.

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

38
Q

Как вы работали с обработкой множественных ошибок?

A

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

Если говорить о том где это использовалось у нас на проектах… Ну вот например, если правильно помню, то подтверждении группы переработок.
Подтверждение состоит из нескольких этапов.
Во-первых, проверяется, проходят проверки по людям, а могут ли они вообще выйти на переработки, с учётом их графика и недавних переработок. Потом создаются документы и назначаются людям.
И вот если либо по кому-то из людей не прошла проверка то через errgroup это всё отлавливается и на фронт возвращается сообщение что вот, такой-то такой-то не может быть в этой группе переработок.

39
Q

Что такое go.mod?

A

go.mod это файл, описывающий текущий проект. В нём можно указать название проекта, версию Golang, с которой он должен работать, А также импорты проекта, то есть сторонние пакеты, которые откуда-то загружаются.

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

40
Q

Как инициализировать модуль в Golang?

A

Через go mod init …

41
Q

Как добавить зависимость в проект?

A

Использовать go get …
Либо, go mod tidy, в этом случае будут актуализированы текущие загруженные пакеты из go.mod.
Будут удалены удалённые из go.mod импорты, и будут скачаны новые.

42
Q

Как удалить зависимость из проекта?

A

Удалить все места в коде где она используется, а потом прописать go mod tidy.

43
Q

Как обновить версию импортируемого пакета?

A

С помощью go get -u …
Тогда пакет обновится до последней минорной версии

44
Q

Как работает go mod tidy?

A

go mod tidy актуализирует зависимости в go.mod файле. Если в проекте используются зависимости, но их нет в go.mod, они будут добавлены и скачаны. Если в go.mod есть зависимости, которые не используются в проекте, они будут удалены.

45
Q

Как работает go mod download?

A

go mod download скачивает все зависимости в локальный кеш.
Это может быть нужно, например, чтобы работать оффлайн, или просто предварительно загрузить все необходимые зависимости.

46
Q

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

A

sync.Mutex, sync.RWMutex, sync.WaitGroup, sync.Once

47
Q

Каким образом указывается файл для тестирования в Golang?

A

Имя файла должно оканчиваться на _test

48
Q

Как запустить тесты в golang?

A

Через использование go test ./… -v

49
Q

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

A

Ну, testing, и testify
Testing использовал, например, для запуска подтестов, или бенчмарков, а testify чтобы была возможность использовать удобные методы для определения, правильно ли идёт тест.
Обычно используется assert

50
Q

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

A

Запустить go test с флагом -cover
Ещё можно записать результаты проверки покрытия в отдельный файл с использованием
go test ./… -coverprofile=coverage.txt
а после этого использовать
go tool cover -html coverage.txt -o index.html
для вывода статистики в отдельный html файл для просмотра

Ещё можно настроить некоторые IDE, чтобы они показывали покрытие напрямую внутри себя

51
Q

Как выглядит структура проекта, с которым ты работал?

A

Ну, проект состоит из нескольких частей.
В корне лежат несколько папок.
Первая - это API. Там хранятся .proto файлы, которые описывают контракты этого сервиса.
Ещё есть папка cmd. Там лежит main и некоторые другие файлы, внутри которых описывается логика для запуска сервиса, для работы с кроной, кафкой и так далее, и функции оттуда используются в этом самом main для инициализации.
Кроме них ещё папки middleware, internal и test
В middleware хранятся обработчики-перехватчики, в test - helper фунционал для тестирования, а в internal основной код приложения.
Ну и ещё есть папка для хранения миграций базы данных.

52
Q

Как запускал проект:

A

make.run

53
Q

Как у вас в проекте работали с миграциями?

A

Мы использовали Goose, который обновлял базу данных используя сохранённые в сервисе миграции базы данных.
Ты с помощью специальной команды из make файла создаёшь миграцию, потом в неё пишешь SQL код.

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

54
Q

Как дебажили код?

A

Логирование, трейсинг запросов через jaeger, локально дебаггер и fmt.Println, ещё пару раз использовал pprof.

(Дописать)

Надо было загружать файлы с клиента на сервер.
Я написал код, который бы получал эти файлы с клиента, и потом сохранял бы в хранилище. В общем, я этот функционал реализовал, но во время локального тестирования обнаружил, что у меня процесс занимает неадекватно много памяти.
Использовал pprof, и обнаружил, что забываю удалять временные файлы из tmpfs. Начал удалять, всё починилось.

(ещё был сервис для просмотра статистики профайлера по сервисам в проде)

55
Q

Можно ли ограничить количество запускаемых горутин?

A

Да, можно через паттерн woker pool.

func main() {
const maxGoroutines = 3 // Максимальное количество одновременно работающих горутин
semaphore := make(chan struct{}, maxGoroutines)
var wg sync.WaitGroup

tasks := []int{1, 2, 3, 4, 5, 6, 7}

for _, task := range tasks {
	wg.Add(1)

	// Захват слота в семафоре
	semaphore <- struct{}{}

	// Запуск горутины
	go func(task int) {
		defer wg.Done()
		defer func() { <-semaphore }() // Освобождение слота после завершения

		fmt.Printf("Начало задачи %d\n", task)
		time.Sleep(2 * time.Second) // Эмуляция работы
		fmt.Printf("Завершение задачи %d\n", task)
	}(task)
}

wg.Wait() // Ожидаем завершения всех задач }
56
Q

Зачем нужен враппинг ошибок в Golang?

A

Ну, есть несколько причин.

Во-первых, для добавления дополнительной информации. Допустим, у нас есть базовая ошибка, например document not found
И с помощью враппинга можно добавить информацию о том, какой именно документ не был найдён

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

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

57
Q

В чём разница между errors.Is и errors.As

A

Разница в том, что errors.Is проверяет, что ошибка является конкретным экземпляром или обёрткой над конкретным экземпляром ошибки

errors.As проверят соответствие ошибки или вложенной ошибки конкретному типу ошибки и извлекает её в переменную

58
Q

Что такое код выхода программы?

A

Код выхода это число, которое возвращается программой при её завершении.
Разные числа сообщают о разных причинах выхода.
Например, это был преднамеренный выход, или какая-то ошибка.

59
Q

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

A

Я точно не помню, но вроде бы это была какая-то корпоративная надстройка над GolangCI-Lint

60
Q

Где в Golang лучше поместить описание интерфейса?
В пакете с реализацией, или где интерфейс используется?

A

Там, где он используется