To-do
- atomic, что такое и зачем нужны?
- Как в памяти представляются числа c плавающей точкой в Go?
- Можно ли использовать один и тот же буфер []byte в нескольких горутинах?
- Что такое string builder?
- Что возвращает []?
- Как проверить тип интерфейса?
- Будет ли работать type assertion с generics?
- new vs make
Общие
Зачем могут потребоваться {} дополнительные?
- Объявить локальные переменные там
- Помочь компилятору быстрее скомпилировать (там дерево)
Замыкание (closure), зачем нужны?
GMP паттерн
Сборщик муссора
Когда срабатывает?
Когда в программе не осталось ссылки на объект.
default в select
Если ничего не пришло в канал, то запуск дефолт кейса.
Типы данных
Базовые типы данных
- Integers ( Signed and Unsigned):
- Floating
- Complex
- Byte
- Rune
- String
- Boolean
Чем отличается int от uint?
int
содержит диапазон от отрицательных значений до положительных, тогда как uint
- это диапазон от 0 в строну увеличения положительных значений.
Какой результат получим если разделить int на 0 и float на 0?
Это вопрос с подвохом. Деление int
на 0 в go невозможно и вызовет ошибку компилятора. Тогда как деление float
на 0 дает в своем результате бесконечность.
Что такое руна?
Руна является псевдонимом для типа int32
и обычно используется для хранения символов в Unicode.
Что такое iota?
Это идентификатор, который позволяет создавать последовательные не типизированные целочисленные константы.
Composite Data Types
- Non-Reference Types: Arrays, Structs
- Reference Types: Slices, Maps
- Interface
Выравнивание памяти (struct padding)
Порядок типов в структуре влияет на объем выделенной памяти. Чтобы оптимизировать порядок в структуре нужно указывать объем в обратном порядке. Также можно использовать специальные утилиты:
Slice
Что такое слайс и чем он отличается от массива?
Slice – это структура go, которая включает в себя ссылку на базовый массив, а также две переменные len(length)
и cap(capacity)
.
len
это длина слайса - то количество элементов, которое в нём сейчас находится.cap
- это ёмкость слайса - то количество элементов, которые мы можем записать в слайс сверхlen
без его дальнейшего расширения.- Array - это последовательно выделенная область памяти. Частью типа array является его размер, который в том числе является не изменяемым.
Лучшие практики
- слайс логично проверять на пустоту с помощью
len() == 0
- По возможности, аллоцировать память для слайса
- Если хотим изменить слайс, создаём его копию
- Результат
append
присваивать той же переменной
Как работает базовая функция append
для go?
Функция принимает на вход слайс и переменное количество элементов для добавления в слайс. append
расширяет слайс за пределы его len
, возвращая при этом новый слайс.
Если количество элементов, которые мы добавляем в слайс, не будет превышать cap, вернется новый слайс, который ссылается на тот же базовый массив, что и предыдущий слайс.
Если количество добавляемых элементов превысит cap, то вернется новый слайс, базовым для которого будет новый массив.
То есть, длина и вместимость передаются по значению, но массив значений передается по ссылке. Вследствие этого получается неявное поведение: добавленные элементы не сохранятся в исходный слайс, но изменение существующих останется:
Какой размер массива выделяется под слайс при его расширении за рамки его емкости?
Если отвечать на вопрос поверхностно, то можно сказать, что базовый массив расширяется в два раза от нашей capacity.
Отвечая более емко, следует учесть, что при больших значениях расширение будет не в два раза и будет вычисляться по специальной формуле.
Если развернуть ответ полностью, то это будет звучать примерно так:
- если требуемая
cap
больше чем вдвое исходнойcap
, то новаяcap
будет равна требуемой; - если это условие не выполнено, а также
len
текущего слайса меньше1024
, то новаяcap
будет в два раза больше базовойcap
; - если первое и второе условия не выполнены, то емкость будет увеличиваться в цикле на четверть от базовой емкости пока не будет обработано переполнение. Посмотреть эти условия более подробно можно в исходниках go.
Как слайс передается в функцию: по ссылке или значению?
Длина и вместимость передаются по значению, но массив значений передается по ссылке.
Это означает, что если вы измените элементы в слайсе внутри функции, эти изменения будут видны и в вызывающей функции.
Однако если вы измените сам слайс (например, используя append
так, что ему потребуется новое место в памяти), это не повлияет на исходный слайс в вызывающей функции.
Reference: GoLang Slice в деталях, простым языком - YouTube
Map в Golang
Сама map
в go - это структура, реализующая операции хеширования. При этом, так же как и любую структуру, содержащую ссылки на области памяти, map
необходимо инициализировать.
map
ссылается на такие элементы как bucket
. Каждый bucket
содержит в себе:
- 8 экстра бит, с помощью которых осуществляется доступ до значений в этом
bucket
; - ссылку на следующий коллизионный
bucket
; - 8 пар ключ-значение, уложенных в массив.
Бакет увеличивается, когда заполняется на 6.5 элементами.
Как на самом деле устроен тип Map в Golang? | Golang под капотом - YouTube
Коллизия в map
Так как хэш-функция не идеальна, передав в нее два разных значения мы можем получить один и тот же результат. В случае с бакетами нам нужно два разных значения положить в один и тот же бакет. Это называется коллизией. Для реализации hashmap необходимо иметь алгоритм их разрешения.
В golang используется separate chain.
При коллизии (когда сложили в один бакет), то обходим бакет в поисках ключа.
Зачем аллоцировать память через make?
Почему нельзя взять указатель на элемент map?
- Расширение мапы поломает значение.
- Дизайн сделан через пару ключ-значение
- Непотокобезопасная структура.
Что может быть ключём map?
- int, float64, rune, string, comparable array and structure, pointer, etc.
Структура может быть ключём до тех пор пока не в полях не будет мапы/слайса.
Что будет в map, если не делать присвоение?
Будет паника.
Что такое Sync.map?
Предоставляет атомарный доступ.
Потокобезопасна ли map?
Нет. Для этого нужно:
- Заворачиваем в мьютекс.
- Sync.Map
Что такое эвакуация, и в каком случае она будет происходить?
Эвакуация – это процесс когда map
переносит свои значения из одной области памяти в другую. Это происходит из-за того что число значений в каждом отдельном bucket
максимально равно 8.
В тот момент времени, когда среднее количество значений в bucket
составляет 6.5, go понимает, что размер map
не удовлетворяет необходимому. Начинается процесс расширения map
.
Следует отметить, что сам процесс эвакуации может происходить некоторое время, на протяжение которого новые и старые данные будут связаны.
Что происходит с мапой при передаче через канал?
Когда вы передаете карту (map) через канал в Go, вы фактически передаете ссылку на эту карту, а не ее копию. Это означает, что получатель этой карты из канала будет иметь доступ к той же самой карте в памяти, что и отправитель.
Особенности
-
Передача по ссылке: Когда вы отправляете мапу через канал, вы передаете указатель на эту мапу. Это означает, что как отправитель, так и получатель будут работать с одной и той же картой в памяти.
-
Изменения видны обеим сторонам: Поскольку и отправитель, и получатель работают с одной и той же мапой, любые изменения, внесенные одной стороной, будут видны и другой стороне.
-
Необходима синхронизация: Если несколько горутин одновременно работают с одной и той же мапой (например, одна горутина читает из карты, а другая пишет в нее), это может привести к состоянию гонки. В таких случаях необходимо использовать мьютексы или другие механизмы синхронизации для безопасного доступа к карте.
-
Закрытие канала: Передача мапы через канал не влияет на жизненный цикл самой карты. Даже если канал будет закрыт, карта останется доступной для дальнейшего использования.
Почему нельзя брать ссылку на значение, хранящееся по ключу в map?
map
поддерживает процедуру эвакуации. Значения, хранящиеся в определённой ячейки памяти в текущий момент времени, в следующий момент времени уже могут там не храниться.
Почему не гарантирован порядок обхода?
- Для того, чтобы разработчик не рассчитывал на нее. Для этого требуется лишняя структура, которая бы хранила этот порядок → замедление производительности
- Для безопасности.
Какие есть особенности синтаксиса получения и записи значений в map?
- Получить значение из
map
, которую мы предварительно не аллоцировали нельзя, приложение упадет в панику. - Если ключ не найден в
map
в ответ мы получим дефолтное значение для типа значенийmap
. То есть, для строки - это будет пустая строка, для int - 0 и так далее. Для того, чтобы точно понять, что вmap
действительно есть значение, хранящееся по переданному ключу, необходимо использовать специальный синтаксис. А именно, возвращать не только само значение, но и булевую переменную, которая показывает удалось-ли получить значение по ключу.
Как происходит поиск по ключу в map?
- вычисляется хэш от ключа;
- с помощью значения хэша и размера
bucket
вычисляется используемый для храненияbucket
; - вычисляется дополнительный хэш - это первые 8 бит уже полученного хэша;
- в полученном
bucket
последовательно сравнивается каждый из 8 его дополнительных хэшей с дополнительным хэшем ключа; - если дополнительные хэши совпали, то получаем ссылку на значение и возвращаем его;
- если дополнительные хэши не совпали, и в
bucket
больше нет дополнительных хэшей, алгоритм переходит в следующийbucket
, ссылка на который хранится в текущем; - если в текущем
bucket
нет ссылки на следующийbucket
, а значение так и не найдено, возвращается дефолтное значение.
Может ли slice быть быстрее map в переборе?
Конструкция слайса такова, что нет указателей на предыдущей или следующий элемент, что позволяет использовать кэши PCA.
Как словить race condition?
Есть флаг race condition – `$ go run -race mysrc.go
Как реализовано ООП в go?
В go нет классической реализация ООП, так как он не объектно-ориентированный язык. При этом в go есть свои приближения к этой реализации.
Наследование
Есть структуры – это специальные типы, в которые мы можем включать другие типы, в том числе такие же структуры. При этом методы дочерних структур родительская структура также будет наследовать.
Composition
Embedding
Что будет, если и в родительской и дочерней структуре есть реализация методов с одинаковым названием? – Реализация родительского метода будет переписана реализацией дочернего метода
Child
Инкапсуляция
Инкапсуляция в go - это возможность задавать переменным, функциям и методам первую букву названия в верхнем или нижнем регистре. Соответственно нижний регистр будет значить, что переменная, функция или метод доступна только в рамках пакета. Тогда как верхний регистр даст доступ к переменной, функции или методу за рамками пакета.
Полиморфизм
Полиморфизм в go реализован с помощью интерфейсов.
Операторы в Go
Можно ли выполнить несколько условий в одном объявленном операторе switch case?
Такое возможно благодаря ключевому слову fallthrough
. Оно заставляет выполнять код в следующей объявленной булевой секции, вне зависимости подходит ли булевое условие case
этой секции.
Интерфейсы в Golang
Что такое интерфейсы в go?
Интерфейс – контракт, что тот или иной объект будет реализовывать указанное в интерфейсе поведение.
Нужны для:
- Чтобы можно было использовать моки
- Уменьшение дублирование кода
- Для реализации полиморфмизма. То есть когда разные типы данных, но у метода схожее поведение.
Что такое пустой интерфейс?
Исходя из определения интерфейса, пустой интерфейс – это интерфейс, для реализации которого не нужно описывать ни одного метода. Таким образом, пустому интерфейсу соответствует абсолютно любой тип.
Что такое nil интерфейс?
Интерфейсное значение считается “нилом”, если и его тип, и его значение равны nil
.
Но интерфейсное значение, которое содержит указатель на некоторый тип, который в свою очередь равен nil
, не считается nil
интерфейсом!
Как преобразовать интерфейс к другому типу?
Интерфейс можно преобразовать в базовый тип значения (скастить). Для этого используется синтаксис, возвращающий две переменные, одна из которых булевая.
В случае, если не удалось скастить интерфейс, булевая переменная будет ложной, а переменная базового типа, к которому приводим интерфейс будет равна дефолтному значению этого типа.
Как определить тип интерфейса?
С помощью инструкции switch case
можно определить тип интерфейса, указав возможные варианты базового типа его значения.
Инструкция defer
Зачем используется ключевое слово defer в go?
В Go есть ключевое слово defer, которое позволяет отложить выполнение метода до выхода их функции, а также для того чтобы ловить паники через recover
.
Как передаются значения в функции, перед которыми указано ключевое слово defer?
Аргументы функций, перед которыми указано ключевое слово defer
оцениваются немедленно. То есть на тот момент, когда переданы в функцию.
Конкуретность
Горутины
Горутина (goroutine) — это функция, выполняющаяся конкурентно с другими горутинами в том же адресном пространстве. Легковесны, потому что они управляются рантаймом языка, а не операционной системой.
Преимущества горутин
- Они легковесны. Их называют легковесными потоками, потому что они управляются рантаймом языка, а не операционной системой. Стоимость переключения контекста и расход памяти намного ниже, чем у потоков ОС.
- Легко и без проблем масштабируют.
- Требуют меньше памяти (2KB).
Каждая Машина работает в отдельном потоке и способна выполнять только одну горутину в момент времени.
Что происходит при написании go?
runtime.newproc(fn)
:- Получаем текущий процессор и текущую горутину
- Создаем новую горутину
- Вешаем нашу целевую функцию на горутину
- Добавить горутину в массив горутин Ничего не запускается!
schedule()
- Проверяем нужно ли выполнить сборку мусора.
- Берем горутину из нашей текущей очереди на нашем треде.
- Если в текущей очереди на нашем треде ничего нету, то мы либо “крадем” горутину из других тредов либо ждем.
Как завершить много горутин?
Несколько методов:
-
Использование канала для завершения: Один из распространенных способов - это использовать канал для уведомления горутин о завершении. Вы можете создать канал и передать его каждой горутине. Когда горутина завершается, она может отправить сообщение в канал. Главная горутина может ожидать, когда все горутины завершатся, считывая из канала.
-
Использование контекста для отмены: Вы также можете использовать контекст для управления завершением горутин. Вы создаете контекст и передаете его каждой горутине. Когда вы хотите завершить все горутины, вы отменяете контекст, и горутины могут проверить отмену и завершить свою работу.
Горутины и потоки разница
Потоки | Горутины |
---|---|
Потоки ОС управляются ядром ОС | Горутины управляются “рантаймом” Go |
Потоки ОС в основном имееют фиксированый размер в 1-2MB | Горутины обычно имеют размер стэка 2KB |
Размер стэка определяется во время компиляции и не может увеличиватся | Размер стэка определяется во время рантайма и может расти вплоть до 1GB что возможно благодаря аллокации и освобождения места из хипа |
У потоков нет простого способа коммуникации между собой. Такая коммуникация имеет большую задержку | Горутины используют “каналы” для быстрого общения между собой с маленькой задержкой |
Scheduler
- М - Машина
- G – Горутина
- P - Процессор
Цель планировщика (scheduler) в том, чтобы распределять готовые к выполнению горутины (G) по свободным машинам (M).
Готовые к исполнению горутины выполняются в порядке очереди, то есть FIFO (First In, First Out). Исполнение горутины прерывается только тогда, когда она уже не может выполняться: то есть из-за системного вызова или использования синхронизирующих объектов (операции с каналами, мьютексами и т.п.).
Не существует никаких квантов времени на работу горутины, после выполнения которых она бы заново возвращалась в очередь. Чтобы позволить планировщику сделать это, нужно самостоятельно вызвать runtime.Gosched()
.
Как работает планировщик?
Внутри функции schedule()
:
- Проверяем нужно ли выполнить сборку мусора.
- Берем горутину из нашей текущей очереди на нашем треде.
- Если в текущей очереди на нашем треде ничего нету, то мы либо “крадем” горутину из дргуих тредов либо ждем.
Что такое deadlock?
Как планировщик определяет deadlock?
-
Проверка активных горутин: Если все горутины находятся в состоянии ожидания и нет активных горутин, которые могли бы выполнить работу, среда выполнения Go определяет это как deadlock. В таком случае Go паникует и выводит сообщение о дедлоке.
-
Ожидание главной горутины: Если главная горутина завершается, но другие горутины продолжают работать или ожидают (например, на блокировке мьютекса или канале), Go также считает это дедлоком.
-
Ожидание на глобальных блокировках: Если горутина ожидает глобальную блокировку (например, блокировку пакета
sync
), и нет других горутин, которые могли бы освободить эту блокировку, Go также определяет это как дедлок.
Многозадачность в Golang
Теперь как в ОС – не кооперативная (вытесняющая) многозадачность.
Подробнее о многозадачности – Многозадачность и её виды
Состояния Горутин
Точно так же, как потоки, у горутин есть те же три состояния высокого уровня. Они определяют роль, которую планировщик Go играет с любой горутиной. Горутина может находиться в одном из трех состояний:
- Ожидание: это означает, что горутина остановлена и ждет чего-то, чтобы продолжить. Это может происходить по таким причинам, как ожидание операционной системы (системные вызовы) или синхронизация вызовов (атомарные и мьютексные операции). Эти типы задержек являются основной причиной плохой производительности.
- Готовность: это означает, что горутина хочет получить время, чтобы выполнить назначенные инструкции. Если у вас много горутин, которым нужно время, то горутине придется ждать дольше, чтобы получить время. Кроме того, индивидуальное количество времени, которое получает любая горутина, сокращено, поскольку больше горутин конкурируют за время. Этот тип задержки планирования также может быть причиной плохой производительности.
- Выполнение: это означает, что горутина была помещена в M и выполняет свои инструкции. Работа, связанная с приложением, завершена. Это то, что все хотят.
Могут ли горутины воровать у друг друга задачи?
Да, могут.
Ограничения планировщика
- LIFO
- Нету гарантии времени выполнения.
- Горутины перемещаются между тредами – снижение эффективности кэшей.
Каналы
Что такое канал?
Виды:
- Буферизированный (кидаем пока есть пространство)
- Небуферизированный (сразу лочится)
Как устроены каналы под капотом?
Буферизированный канал
Обычно каналы работают синхронно - каждая из сторон ждёт, когда другая сможет получить или передать сообщение. Но буферизированный канал работает асинхронно — получение или отправка сообщения не заставляют стороны останавливаться. Но канал теряет пропускную способность, когда он занят, в данном случае, если мы отправим в канал 1 сообщение, то мы не сможем отправить туда ещё одно до тех пор, пока первое не будет получено.
WaitGroups
Это механизм синхронизации в языке программирования Go (Golang), который позволяет дождаться завершения выполнения нескольких горутин (concurrent goroutines) перед продолжением выполнения основной программы.
WaitGroup имеет три основных метода:
Add(delta int)
: Инкрементирует счетчик WaitGroup на значениеdelta
. Обычно используется перед запуском каждой горутины.Done()
: Декрементирует счетчик WaitGroup на 1. Обычно вызывается в конце выполнения каждой горутины, чтобы указать, что она завершила свою работу.Wait()
: Блокирует выполнение программы до тех пор, пока счетчик WaitGroup не станет равным нулю. Этот метод используется в основной горутине, чтобы ожидать завершения всех других горутин.
Graceful shutdown
Корректное и безопасное завершение работы программы или сервиса, позволяя всем активным операциям завершиться перед выходом. Это особенно важно для серверных приложений, чтобы избежать потери данных или состояния.
Можно создать канал, который будет принимать значения SIGINT
(Ctrl+C) или SIGTERM
(посылаются при выполнении команды kill
). И при получении его server.shutdown()
.
Что такое захват переменной?
В функциональных языках программирования переменные, определенные внутри функции, могут иметь доступ к переменным из внешней области видимости. Когда функция захватывает переменную из окружающего контекста, она сохраняет доступ к этой переменной даже после завершения выполнения функции. Это позволяет использовать значения переменных из внешнего контекста внутри функции даже после того, как эта функция была вызвана.
Где используется контекст?
- Таймауты
- Отмена операций (напр., горутин)
Generics
Generics - это концепция языков программирования, которая позволяет писать функции и структуры данных, не привязываясь к конкретным типам данных, а вместо этого параметризуя их типами.
То есть можно писать функции для разных типов данных. Это как шаблоны в С++.
Объявление переменных – разница
Map
Если объявить так: var m map[string]int
и потом присвоить значение, то будет паника.
Slice
Если в пустой слайс загонять через append
, то все нормально. Если в пустой слайс, где нет еще capacity, то будет паника.
Мьютексы
Механизм синхронизации, который используется для обеспечения доступа к общим данным из нескольких горутин.
Два основных:
- sync.Mutex: Это классический мьютекс для обеспечения взаимного исключения. Он блокирует доступ к общим данным, позволяя только одной горутине захватить его одновременно.
- sync.RWMutex: Это мьютекс с поддержкой чтения/записи (read-write lock). Он позволяет нескольким горутинам читать данные одновременно или одной горутине писать.
Escape Analysis, Стэк, Куча
Info
Escape analysis (анализ утечек) — это компиляторный процесс в Go (или Golang), который определяет, где должны размещаться переменные: на стеке или в куче.
Когда вы создаете переменную в Go, она обычно размещается на стеке, который является быстрым и эффективным местом для хранения данных, потому что когда функция завершает свою работу, все ее переменные на стеке автоматически освобождаются. Однако, если переменная “утекает” за пределы своей области видимости или ее жизненный цикл превышает время жизни функции, в которой она была создана, то она должна быть размещена в куче. Переменные в куче управляются сборщиком мусора и имеют более длительный жизненный цикл.
Escape analysis помогает компилятору Go принимать решения о том, где размещать переменные. Если компилятор определяет, что переменная “утекает” за пределы своей области видимости, он размещает ее в куче.
Чтобы увидеть результат escape analysis компилятора Go, вы можете скомпилировать программу с флагом -gcflags='-m'
:
Зачем нужен он нужен?
- Оптимизация производительности: Понимание того, когда и почему переменные размещаются в куче, может помочь в оптимизации производительности. Переменные, размещенные в стеке, обычно создаются и уничтожаются быстрее, чем те, что в куче. Кроме того, чем меньше объектов вы размещаете в куче, тем меньше работы для сборщика мусора.
- Понимание ошибок: В некоторых случаях неправильное понимание того, где переменная размещается, может привести к ошибкам. Например, если вы возвращаете указатель на локальную переменную из функции, ожидая, что она будет размещена в куче, но на самом деле она размещена в стеке, это может привести к неопределенному поведению.
- Оптимизация использования памяти: Если вы работаете с ограниченными ресурсами или хотите минимизировать использование памяти, понимание escape analysis может помочь вам принимать решения о структуре вашего кода.
Стек и Куча
Стек характеристики
- Быстрый: Доступ к данным на стеке обычно быстрее, чем к данным в куче.
- Локальные переменные: Локальные переменные функций обычно размещаются на стеке.
- Автоматическое управление: Когда функция завершается, все ее локальные переменные автоматически освобождаются. Это делает управление памятью на стеке очень эффективным.
- Ограниченный размер: Стек имеет ограниченный размер. Если ваша программа использует слишком много памяти на стеке (например, из-за глубокой рекурсии), это может привести к ошибке переполнения стека.
- LIFO (Last-In-First-Out): Данные добавляются и удаляются из стека в порядке LIFO.
Куча характеристики
- Динамическое выделение: Когда вам нужно динамически выделить память (например, для массива, размер которого определяется во время выполнения), эта память выделяется в куче.
- Глобальные данные: Глобальные переменные и данные, которые должны существовать вне вызовов функций, обычно размещаются в куче.
- Управление сборщиком мусора: В Go куча управляется сборщиком мусора, который автоматически освобождает неиспользуемую память.
- Медленнее стека: Выделение и освобождение памяти в куче обычно медленнее, чем на стеке.
- Больший размер: Куча обычно имеет гораздо больший размер, чем стек, и может расти в зависимости от доступной системной памяти.
В Go, как и во многих современных языках программирования, большинство решений о том, где размещать данные (на стеке или в куче), принимаются компилятором. Компилятор Go использует “escape analysis” для определения, должны ли переменные размещаться на стеке или в куче. Если компилятор определяет, что переменная “убегает” из своей локальной области видимости, он размещает ее в куче.
Sharing up vs. Sharing down:
Передача указателей вверх по стеку может привести к выделению памяти в куче, в то время как передача указателей вниз по стеку, как правило, этого не делает
-
Sharing up: Когда вы передаете указатель “вверх” по стеку (например, возвращая указатель из функции), компилятор может решить, что переменная должна быть размещена в куче, чтобы избежать ошибок висячих указателей.
Если функция
B()
возвращает указатель на свои локальные данные функцииA()
, это называется “делиться вверх по стеку”. Это проблематично, потому что когдаB()
завершится, ее данные будут удалены со стека, ноA()
все еще будет иметь указатель на эти данные. Чтобы избежать проблем, компилятор Go выделяет эти данные в куче, а не на стеке.+-----+ | A | ⇐ имеет указатель на данные B после завершения B +-----+ | B | ⇐ верх стека (но завершилась и ее данные убраны) +-----+ | .…. |
-
Sharing down: Когда вы передаете указатель “вниз” по стеку (например, передавая указатель в функцию как аргумент), переменная может оставаться в стеке, так как ее жизненный цикл явно ограничен областью видимости вызывающей функции.
Если функция
A()
передает указатель на свои данные функцииB()
, это называется “делиться вниз по стеку”.+-----+ | B | ⇐ верх стека +-----+ | A | +-----+ | ..… |
Зачем использовать в структуре указатель?
Тестирование
….
Посмотреть позже
- Channel Axioms | Dave Cheney
- Scheduling In Go : Part I - OS Scheduler
- Как на самом деле устроен тип Map в Golang? | Golang под капотом - YouTube
- Before you continue to YouTube
- ТОП вопросов на Go собеседовании | Вопросы и задачи с реальных собеседований - YouTube
- Golang-Senior-Developer-Interview - Arkusze Google
- Антон Сергеев, «Go под капотом» - YouTube
- Полезные ресурсы для изучающих Go · GitHub
- Мой опыт технического собеседования: как не превратить его в экзамен и найти профпригодного сотрудника
- Awesome Software Architecture