Forwarded from Proglib.academy | IT-курсы
Мы подготовили нетривиальные задачи математического и логического характера с собеседований, чтобы помочь вам лучше подготовиться к следующим вызовам. В статье вы найдете задачи, которые нередко встречаются на интервью и могут стать настоящим испытанием даже для опытных разработчиков.
🔗 Читать статью
Please open Telegram to view this post
VIEW IN TELEGRAM
🥱2
💬 Как устроено фаззинг-тестирование в Go?
Фаззинг представляет собою технологию автоматизированного поиска ошибок с помощью случайных входных данных и анализа реакции программы на них. Она полезна, если нужно проверить граничные условия или корректность обработки потока ввода — то есть тогда, когда нужно найти значения, при которых «падает» программа. В Go 1.18 была введена встроенная поддержка фаззинг-тестирования.
📌 Основные правила для фаззинг-тестов в Go:
1. Название метода должно начинаться с
2. Название файла с фаззинг-тестами:
3. Фаззинг target должна быть вызовом метода
4. В одном фаззинг-тесте должна быть ровно одна фаззинг target.
5. Все элементы seed corpus должны иметь типы, идентичные аргументам для фаззинга, в том же порядке. Это касается вызовов
6. Аргументы для фаззинга могут быть только следующих типов:
-
-
-
-
-
Фаззинг представляет собою технологию автоматизированного поиска ошибок с помощью случайных входных данных и анализа реакции программы на них. Она полезна, если нужно проверить граничные условия или корректность обработки потока ввода — то есть тогда, когда нужно найти значения, при которых «падает» программа. В Go 1.18 была введена встроенная поддержка фаззинг-тестирования.
📌 Основные правила для фаззинг-тестов в Go:
1. Название метода должно начинаться с
FuzzXxx
, принимать только *testing.F
в качестве аргумента и не возвращать значение.2. Название файла с фаззинг-тестами:
*_test.go
.3. Фаззинг target должна быть вызовом метода
(*testing.F).Fuzz
, который принимает *testing.T
в качестве первого параметра, за которым следуют аргументы для фаззинга (не возвращает значение).4. В одном фаззинг-тесте должна быть ровно одна фаззинг target.
5. Все элементы seed corpus должны иметь типы, идентичные аргументам для фаззинга, в том же порядке. Это касается вызовов
(*testing.F).Add
и любых файлов corpus в директории testdata/fuzz
.6. Аргументы для фаззинга могут быть только следующих типов:
-
string
, []byte
-
int
, int8
, int16
, int32/rune
, int64
-
uint
, uint8/byte
, uint16
, uint32
, uint64
-
float32
, float64
-
bool
👍5
💬 Как реализовать CQRS и Event Sourcing на Go?
CQRS — это паттерн, который разделяет ответственность за чтение и запись данных. Это позволяет разным частям системы обрабатывать различные задачи, например, использовать разные базы данных для чтения и записи или использовать разные модели данных для чтения и записи.
Event Sourcing — это паттерн, который включает в себя хранение всех изменений состояния системы в виде последовательности событий. Это позволяет восстанавливать состояние системы в любой момент времени путем воспроизведения событий.
📌 Простой пример реализации с использованием библиотеки eventstore представлен выше.
👉 Знакомство с CQRS путем рефакторинга Go-проекта
👉 Реализация на чистом Go
CQRS — это паттерн, который разделяет ответственность за чтение и запись данных. Это позволяет разным частям системы обрабатывать различные задачи, например, использовать разные базы данных для чтения и записи или использовать разные модели данных для чтения и записи.
Event Sourcing — это паттерн, который включает в себя хранение всех изменений состояния системы в виде последовательности событий. Это позволяет восстанавливать состояние системы в любой момент времени путем воспроизведения событий.
📌 Простой пример реализации с использованием библиотеки eventstore представлен выше.
👉 Знакомство с CQRS путем рефакторинга Go-проекта
👉 Реализация на чистом Go
🔥5👍2❤1
Forwarded from Библиотека Go-разработчика | Golang
⚡️ Паттерн Transactional Outbox: теория и практика от Николая Тузова
⌛ Таймкоды:
00:00 Какую проблему мы решаем
05:47 Нам нужна атомарность
07:03 Про Two-Phase Commit
07:36 NoSQL базы данных
09:59 Гарантия доставки - "At Least Once"
11:48 Практика: пишем Outbox для сокращателя ссылок
12:43 Storage: сохраняем сообщения в таблицу
28:01 Event Sender: отправка сообщений из таблицы
36:52 Подключаем Event Sender
39:09 Тестируем отправку сообщений
41:12 Итоги
📺 Смотреть полностью
⌛ Таймкоды:
00:00 Какую проблему мы решаем
05:47 Нам нужна атомарность
07:03 Про Two-Phase Commit
07:36 NoSQL базы данных
09:59 Гарантия доставки - "At Least Once"
11:48 Практика: пишем Outbox для сокращателя ссылок
12:43 Storage: сохраняем сообщения в таблицу
28:01 Event Sender: отправка сообщений из таблицы
36:52 Подключаем Event Sender
39:09 Тестируем отправку сообщений
41:12 Итоги
📺 Смотреть полностью
👍14❤1
💬 Чем observability (наблюдаемость) отличается от «традиционного» мониторинга?
Мониторинг фокусируется на вопросах, ответы на которые помогут определить или предсказать некоторые ожидаемые или ранее имевшие место режимы отказа. Проще говоря, мониторинг сосредоточен на «известных неизвестных».
Предполагается, что система будет вести себя — и, следовательно, выходить из строя — определенным и предсказуемым образом. Когда обнаруживается новый режим отказа, его симптомы добавляются в пакет мониторинга, и процесс начинается сначала.
Мониторинг — это наши действия в системе, которые предпринимаются с целью узнать, работает она или не работает. Методы observability, напротив, основной упор делают на понимание системы, позволяющее соотносить события и поведение. Observability — это свойство системы, которое позволяет спросить, почему она не работает.
Три столпа observability — это собирательное название, под которым иногда упоминаются три наиболее распространенных (и основополагающих) инструмента — логирование, метрики и трассировка.
Мониторинг фокусируется на вопросах, ответы на которые помогут определить или предсказать некоторые ожидаемые или ранее имевшие место режимы отказа. Проще говоря, мониторинг сосредоточен на «известных неизвестных».
Предполагается, что система будет вести себя — и, следовательно, выходить из строя — определенным и предсказуемым образом. Когда обнаруживается новый режим отказа, его симптомы добавляются в пакет мониторинга, и процесс начинается сначала.
Мониторинг — это наши действия в системе, которые предпринимаются с целью узнать, работает она или не работает. Методы observability, напротив, основной упор делают на понимание системы, позволяющее соотносить события и поведение. Observability — это свойство системы, которое позволяет спросить, почему она не работает.
Три столпа observability — это собирательное название, под которым иногда упоминаются три наиболее распространенных (и основополагающих) инструмента — логирование, метрики и трассировка.
👍7🥱2
💬 Какие особенности необходимо учитывать при работе с функциями context.WithValue и context.Value?
Они позволяют определять и получать произвольные пары ключ/значение, которые могут использоваться процессами, обрабатывающими запросы.
Однако, по утверждениям разработчиков, эта функциональность ортогональна функциональности отмены долгоживущих запросов, усложняет анализ потока выполнения программы и может легко нарушить связи во время компиляции.
👉 Подробнее
Они позволяют определять и получать произвольные пары ключ/значение, которые могут использоваться процессами, обрабатывающими запросы.
Однако, по утверждениям разработчиков, эта функциональность ортогональна функциональности отмены долгоживущих запросов, усложняет анализ потока выполнения программы и может легко нарушить связи во время компиляции.
👉 Подробнее
💬 Какие существуют распространенные реализации шаблона запрос/ответ?
🔸 REST — это архитектурный стиль для создания сетевых приложений, основанный на принципах HTTP. Он предполагает использование стандартных HTTP-методов (GET, POST, PUT, DELETE и т. д.) для выполнения операций над ресурсами, идентифицируемыми с помощью URL.
🔸 Вызов удаленных процедур (Remote Procedure Calls, RPC). RPC-фреймворки позволяют программам запускать процедуры в другом адресном пространстве (на удалённых узлах, либо в независимой сторонней системе на том же узле). В Go имеется стандартная реализация RPC в форме пакета
🔸 GraphQL — язык запросов и управляющих воздействий, который часто считают альтернативой REST. Он особенно эффективен при работе со сложными наборами данных.
👉 А ваша служба является RESTful? Все что необходимо/обязательно знать про веб службы и REST (читать)
🔸 REST — это архитектурный стиль для создания сетевых приложений, основанный на принципах HTTP. Он предполагает использование стандартных HTTP-методов (GET, POST, PUT, DELETE и т. д.) для выполнения операций над ресурсами, идентифицируемыми с помощью URL.
🔸 Вызов удаленных процедур (Remote Procedure Calls, RPC). RPC-фреймворки позволяют программам запускать процедуры в другом адресном пространстве (на удалённых узлах, либо в независимой сторонней системе на том же узле). В Go имеется стандартная реализация RPC в форме пакета
net/rpc
. Есть также две крупные реализации RPC, поддерживающие разные языки программирования: Apache Thrift и gRPC. Несмотря на сходство целей и архитектуры, gRPC, пользуется большей популярностью в сообществе. 🔸 GraphQL — язык запросов и управляющих воздействий, который часто считают альтернативой REST. Он особенно эффективен при работе со сложными наборами данных.
👉 А ваша служба является RESTful? Все что необходимо/обязательно знать про веб службы и REST (читать)
Хабр
А ваша служба является RESTful? Все что необходимо/обязательно знать про веб службы и REST
Введение Вот не люблю я изобретать велосипед и статью я бы эту не написал, но пришлось. Про REST сказано уже довольно много. Многие поставщики веб служб готовы клясться, что их службы являются...
👍3❤2
💬 Как создать альтернативу bytes.Buffer, которая реализует интерфейс io.ReadWriter, позволяет узнать длину и емкость буфера и занимает меньше памяти, чем bytes.Buffer?
Для этой задачи можно использовать новый тип, основанный на срезе байт. Вот пример реализации:
В примере создается тип Buffer, который является срезом байт (`[]byte`). Методы Write и Read реализованы для добавления данных в буфер и чтения данных из буфера соответственно.
👉 Другое решение можно найти здесь
Для этой задачи можно использовать новый тип, основанный на срезе байт. Вот пример реализации:
// Buffer — буфер байтов переменного размера с методами Read и Write
// Нулевое значение Buffer — пустой буфер, готовый к использованию
type Buffer []byte
// Write записывает len(p) байт из p в Buffer
func (b *Buffer) Write(p []byte) (int, error) {
*b = append(*b, p...)
return len(p), nil
}
// Read читает до len(p) байт в p из Buffer
func (b *Buffer) Read(p []byte) (int, error) {
if len(p) == 0 {
return 0, nil
}
if len(*b) == 0 {
return 0, io.EOF
}
n := copy(p, *b)
*b = (*b)[n:]
return n, nil
}
В примере создается тип Buffer, который является срезом байт (`[]byte`). Методы Write и Read реализованы для добавления данных в буфер и чтения данных из буфера соответственно.
👉 Другое решение можно найти здесь
GitHub
GitHub - extemporalgenome/minimembuf: Toy answer to http://dave.cheney.net/2015/06/05/friday-pop-quiz-the-smallest-buffer
Toy answer to http://dave.cheney.net/2015/06/05/friday-pop-quiz-the-smallest-buffer - extemporalgenome/minimembuf
👍3❤1🔥1
💬 Как Go обновляет сторонние пакеты?
В Go для управления сторонними пакетами используется инструмент
С появлением модулей Go (Go 1.11+), использование
Для обновления зависимостей в проекте с модулями Go используется команда
По умолчанию Go добавляет последнюю доступную версию пакета. Чтобы проверить, какие еще версии пакета доступны, используется команда
Чтобы изменить версию конкретного пакета до версии vX.X.X, используется следующая команда:
Обновляем указанные зависимости:
В Go для управления сторонними пакетами используется инструмент
go get
или модули Go. В более ранних версиях Go до введения модулей, для установки и обновления сторонних пакетов использовалась команда go get
. Она скачивала и устанавливала пакеты из удаленного репозитория в $GOPATH
.С появлением модулей Go (Go 1.11+), использование
go get
для управления зависимостями стало менее распространенным. Вместо этого, мы можем создать и поддерживать файл go.mod
, который описывает зависимости проекта. Для обновления зависимостей в проекте с модулями Go используется команда
go get -u
или go get -u=patch
для обновления до последней минорной версии или патча соответственно. Это обновление происходит в контексте модульной структуры проекта и сохраняет совместимость версий зависимостей.По умолчанию Go добавляет последнюю доступную версию пакета. Чтобы проверить, какие еще версии пакета доступны, используется команда
go list
. По умолчанию она выдает адрес текущего пакета, по которому его можно импортировать. Чтобы изменить версию конкретного пакета до версии vX.X.X, используется следующая команда:
go get github.com/example/repo@vX.X.X
Обновляем указанные зависимости:
go get -u <package-name>
👍9
💬 В чем суть протокола веб-сокет и в чем преимущество Go при работе с веб-сокетами?
Веб-сокет — это протокол поверх TCP, обеспечивающий двустороннюю связь между браузером и сервером через постоянное соединение. Go благодаря своей простоте и поддержке конкурентности идеально подходит для работы с веб-сокетами.
Горутины в Go более эффективны по сравнению с традиционными потоками операционной системы благодаря меньшему потреблению памяти и более низким накладным расходам на создание и управление ими.
Каналы в Go обеспечивают безопасную и эффективную синхронизацию между горутинами без явного использования блокировок или условий состояния, что важно для обработки данных в режиме реального времени, таких как обмен сообщениями.
Используя горутины, Go позволяет обрабатывать одновременно множество активных веб-сокет соединений, обеспечивая высокую производительность и отзывчивость при минимальных накладных расходах.
Это делает Go идеальным выбором для реализации веб-сокет серверов, где требуется эффективная обработка и обмен сообщениями между клиентом и сервером в реальном времени.
Веб-сокет — это протокол поверх TCP, обеспечивающий двустороннюю связь между браузером и сервером через постоянное соединение. Go благодаря своей простоте и поддержке конкурентности идеально подходит для работы с веб-сокетами.
Горутины в Go более эффективны по сравнению с традиционными потоками операционной системы благодаря меньшему потреблению памяти и более низким накладным расходам на создание и управление ими.
Каналы в Go обеспечивают безопасную и эффективную синхронизацию между горутинами без явного использования блокировок или условий состояния, что важно для обработки данных в режиме реального времени, таких как обмен сообщениями.
Используя горутины, Go позволяет обрабатывать одновременно множество активных веб-сокет соединений, обеспечивая высокую производительность и отзывчивость при минимальных накладных расходах.
Это делает Go идеальным выбором для реализации веб-сокет серверов, где требуется эффективная обработка и обмен сообщениями между клиентом и сервером в реальном времени.
👍8🥱2
🧑💻 Статьи для IT: как объяснять и распространять значимые идеи
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
Напоминаем, что у нас есть бесплатный курс для всех, кто хочет научиться интересно писать — о программировании и в целом.
Что: семь модулей, посвященных написанию, редактированию, иллюстрированию и распространению публикаций.
Для кого: для авторов, копирайтеров и просто программистов, которые хотят научиться интересно рассказывать о своих проектах.
👉Материалы регулярно дополняются, обновляются и корректируются. А еще мы отвечаем на все учебные вопросы в комментариях курса.
👍1
💬 Как в Go вернуть из функции ошибку, не импортируя дополнительных пакетов, даже стандартных?
📌 Простой пример:
🔷 Структура
🔷 Метод
🔷 Функция принимает два параметра типа
🔷 Если второй аргумент
🔷 Результат и ошибка, возвращаемые функцией, проверяются. Если ошибка существует, она обрабатывается (в данном случае выводится на экран с помощью
📌 Простой пример:
package main
// CustomError определяет структуру для ошибки
type CustomError struct {
message string
}
// Error реализует интерфейс error для CustomError
func (e *CustomError) Error() string {
return e.message
}
// Функция, которая может вернуть ошибку
func divide(a, b float64) (float64, *CustomError) {
if b == 0 {
// Создание и возврат ошибки
return 0, &CustomError{message: "division by zero"}
}
return a / b, nil
}
func main() {
result, err := divide(4, 0)
if err != nil {
// Обработка ошибки
println("Error:", err.Error())
} else {
println("Result:", result)
}
}
🔷 Структура
CustomError
содержит одно поле message
типа string
, которое будет хранить сообщение об ошибке.🔷 Метод
Error
возвращает сообщение об ошибке. Это позволяет CustomError
удовлетворять интерфейсу error
.🔷 Функция принимает два параметра типа
float64 и
возвращает два значения: результат типа float64
и ошибку типа *CustomError
.🔷 Если второй аргумент
b
равен нулю, создается и возвращается ошибка с помощью CustomError
.🔷 Результат и ошибка, возвращаемые функцией, проверяются. Если ошибка существует, она обрабатывается (в данном случае выводится на экран с помощью
println
). Если ошибки нет, выводится результат деления.👍11🥱7😁2💯1
💬 Для чего предназначена функция os.Exit?
Она предназначена для немедленного завершения программы с заданным статусом. Функция завершает выполнение текущего процесса без выполнения отложенных функций (
Go не использует целочисленное значение возврата из функции main для указания статуса выхода. Если нужно завершить программу с ненулевым статусом, следует использовать
Она предназначена для немедленного завершения программы с заданным статусом. Функция завершает выполнение текущего процесса без выполнения отложенных функций (
defer
).Go не использует целочисленное значение возврата из функции main для указания статуса выхода. Если нужно завершить программу с ненулевым статусом, следует использовать
os.Exit
.👍1
💬 Что из себя представляет тип данных string в языке Go? Можно ли изменить определенный символ в строке? Что происходит при конкатенации строк?
Тип данных
При конкатенации строк создается новая строка, которая является комбинацией исходных строк. Это требует выделения нового блока памяти требуемого размера.
Чтобы избавиться от лишних аллокаций, можно воспользоваться типом
Тип данных
string
представляет собой неизменяемую последовательность байтов. То есть строку нельзя изменить после создания, включая изменение отдельных символов. При попытке изменить определенный символ в строке возникает ошибка компиляции.При конкатенации строк создается новая строка, которая является комбинацией исходных строк. Это требует выделения нового блока памяти требуемого размера.
Чтобы избавиться от лишних аллокаций, можно воспользоваться типом
strings.Builder
и методом WriteString
:func join(strs ...string) string {
var sb strings.Builder
for _, str := range strs {
sb.WriteString(str)
}
return sb.String()
}
👍11❤1
Forwarded from Библиотека Go-разработчика | Golang
🏃 Самоучитель по Go для начинающих. Часть 13. Работа с датой и временем. Пакет time
В этой части самоучителя изучим способы работы с датами и временем в языке Go, разберем полезные функции пакета time и в заключение решим парочку интересных задач.
👉 Читать гайд
📌 Остальные части в серии:
1. Особенности и сфера применения Go, установка, настройка
2. Ресурсы для изучения Go с нуля
3. Организация кода. Пакеты, импорты, модули. Ввод-вывод текста.
4. Переменные. Типы данных и их преобразования. Основные операторы
5. Условные конструкции if-else и switch-case. Цикл for. Вложенные и бесконечные циклы
6. Функции и аргументы. Области видимости. Рекурсия. Defer
7. Массивы и слайсы. Append и сopy. Пакет slices
8. Строки, руны, байты. Пакет strings. Хеш-таблица (map)
9. Структуры и методы. Интерфейсы. Указатели. Основы ООП
10. Введение в ООП. Наследование, абстракция, полиморфизм, инкапсуляция
11. Обработка ошибок. Паника. Восстановление. Логирование
12. Обобщенное программирование. Дженерики
В этой части самоучителя изучим способы работы с датами и временем в языке Go, разберем полезные функции пакета time и в заключение решим парочку интересных задач.
👉 Читать гайд
📌 Остальные части в серии:
1. Особенности и сфера применения Go, установка, настройка
2. Ресурсы для изучения Go с нуля
3. Организация кода. Пакеты, импорты, модули. Ввод-вывод текста.
4. Переменные. Типы данных и их преобразования. Основные операторы
5. Условные конструкции if-else и switch-case. Цикл for. Вложенные и бесконечные циклы
6. Функции и аргументы. Области видимости. Рекурсия. Defer
7. Массивы и слайсы. Append и сopy. Пакет slices
8. Строки, руны, байты. Пакет strings. Хеш-таблица (map)
9. Структуры и методы. Интерфейсы. Указатели. Основы ООП
10. Введение в ООП. Наследование, абстракция, полиморфизм, инкапсуляция
11. Обработка ошибок. Паника. Восстановление. Логирование
12. Обобщенное программирование. Дженерики
❗️Вакансии «Библиотеки программиста» — ждем вас в команде!
Мы постоянно растем и развиваемся, поэтому создали отдельную страницу, на которой будут размещены наши актуальные вакансии. Сейчас мы ищем:
👉 авторов в наше медиа proglib.io
👉 контент-менеджеров для ведения телеграм-каналов
Подробности тут.
Мы предлагаем частичную занятость и полностью удаленный формат работы — можно совмещать с основной и находиться в любом месте🌴
Ждем ваших откликов👾
Мы постоянно растем и развиваемся, поэтому создали отдельную страницу, на которой будут размещены наши актуальные вакансии. Сейчас мы ищем:
Подробности тут.
Мы предлагаем частичную занятость и полностью удаленный формат работы — можно совмещать с основной и находиться в любом месте🌴
Ждем ваших откликов
Please open Telegram to view this post
VIEW IN TELEGRAM
💬 Можно ли предугадать, что GC отработает за константное время N?
Нет, невозможно предугадать. Время работы GC зависит от различных факторов, включая количество и сложность объектов в памяти, текущая нагрузка на систему и другие параметры выполнения программы.
Сборщик мусора работает по алгоритмам, которые могут варьироваться по времени выполнения в зависимости от конкретных условий.
Нет, невозможно предугадать. Время работы GC зависит от различных факторов, включая количество и сложность объектов в памяти, текущая нагрузка на систему и другие параметры выполнения программы.
Сборщик мусора работает по алгоритмам, которые могут варьироваться по времени выполнения в зависимости от конкретных условий.
👍11
💬 Какие особенности следует учитывать при сбросе таймеров в Go разных версий?
Go ≤ 1.22: для таймера, созданного с помощью
Go ≥ 1.23: для таймера, созданного с помощью
Для таймера, созданного с помощью
👉 Подробнее
Go ≤ 1.22: для таймера, созданного с помощью
NewTimer
, метод Reset
следует вызывать только на остановленных или завершенных таймерах с drained каналами.Go ≥ 1.23: для таймера, созданного с помощью
NewTimer
, безопасно вызывать метод Reset
на таймерах в любом состоянии (активном, остановленном или завершенном). Очистка канала не требуется, так как канал таймера больше не буферизован (в некотором роде).Для таймера, созданного с помощью
AfterFunc
, метод Reset
либо переназначает выполнение функции (если таймер все еще активен), либо планирует выполнение функции заново (если таймер остановлен или завершен).👉 Подробнее
antonz.org
Resetting timers in Go
Chances are you are doing it wrong.
👍12
💬 Как в Go передаются значения в функции, перед которыми указано defer?
В Go функции, перед которыми указано defer, откладываются до тех пор, пока не завершится текущий блок кода или текущая функция, в которой был вызван
Аргументы для таких функций вычисляются немедленно, но сама функция выполняется позже, когда сработает отложенное действие. Это значит, что все значения аргументов, переданных в defer функцию, фиксируются в момент объявления defer.
Рассмотрим на примере:
В этом коде, хотя x изменяется после объявления defer, отложенная функция fmt.Println получит значение x, которое было в момент объявления defer, то есть 10. Поэтому вывод будет следующим:
В Go функции, перед которыми указано defer, откладываются до тех пор, пока не завершится текущий блок кода или текущая функция, в которой был вызван
defer
. Аргументы для таких функций вычисляются немедленно, но сама функция выполняется позже, когда сработает отложенное действие. Это значит, что все значения аргументов, переданных в defer функцию, фиксируются в момент объявления defer.
Рассмотрим на примере:
package main
import "fmt"
func main() {
x := 10
defer fmt.Println("deferred:", x) // Аргумент x вычисляется сейчас, но вывод откладывается
x = 20
fmt.Println("immediate:", x)
}
В этом коде, хотя x изменяется после объявления defer, отложенная функция fmt.Println получит значение x, которое было в момент объявления defer, то есть 10. Поэтому вывод будет следующим:
immediate: 20
deferred: 10
👍36