go
package main
import (
"fmt"
)
func main() {
funcs := []func(){}
for i := 0; i < 3; i++ {
funcs = append(funcs, func() {
fmt.Println(i)
})
}
for _, f := range funcs {
f()
}
}
❓ Варианты:
A)0 1 2
B)3 3 3
C) Паника на runtime
D) 0 0 0
💡 Пиши свой ответ в комментариях, и объясни почему!
✅ Правильный ответ:
🔍 Почему так происходит:
В этом коде создаётся срез из замыканий, и каждый func() внутри цикла ссылается на одну и ту же переменную i.
Ключевой момент: i не копируется при каждой итерации цикла, а продолжает изменяться, и все функции “запоминают” ссылку на одну и ту же i, а не её значение на момент создания.
К моменту вызова всех функций (f() в конце) цикл уже завершён, и значение i стало 3.
👉 Поэтому каждая функция выводит 3, а не 0, 1, 2.
@golangtests
Please open Telegram to view this post
VIEW IN TELEGRAM
🧪 Задача на Go 1.24: Range и поведение переменной цикла
Что выведет следующий код в Go 1.24?
Варианты ответа:
A)
B)
C)
D)
---
✅ Правильный ответ:A
Почему:
В Go 1.24 при использовании, переменная копируется на каждой итерации, а не переиспользуется. Таким образом, замыкания получают своё собственное значение на каждой итерации.
Здесь возвращает индексы ( , , ), а не значения среза.
Чтобы получить значения ( , , ), нужно использовать .
Что выведет следующий код в Go 1.24?
package main
import "fmt"
func main() {
values := []int{10, 20, 30}
funcs := []func(){}
for v := range values {
funcs = append(funcs, func() {
fmt.Println(v)
})
}
for _, f := range funcs {
f()
}
}
Варианты ответа:
A)
1
2
B)
2
2
C)
20
30
D)
0
0
---
✅ Правильный ответ:
Почему:
В Go 1.24 при использовании
for v := range
v
v
Здесь
range
0
1
2
Чтобы получить значения (
10
20
30
for _, v := range values
ХОЧЕШЬ ПОВЫСИТЬ ГРЕЙД В 2025 ГОДУ? 🚀
Если:
— код разваливается от правок,
— бизнес-логика размазана,
— Entity — просто структуры,
— а тесты живут своей жизнью —
вам точно сюда.
📦 Вы создадите сервис с нуля, разберете ключевые паттерны DDD и научитесь строить архитектуру, устойчивую к изменениям.
🔹 Как проходит обучение?
✅ Практика на реальном кейсе
Вы разработаете сервис диспетчеризации заказов, решая реальные бизнес-задачи.
✅ Видеоуроки в удобное время
Эксперт шаг за шагом создаст микросервис «Корзина», раскрывая ключевые паттерны DDD.
✅ Чат с экспертом
Не останетесь без ответов – задавайте вопросы в закрытом чате и получайте разбор сложных тем.
✅ Персональная обратная связь
Куратор проведет ревью кода, даст рекомендации и поможет разобраться с архитектурными решениями.
✅ Вебинары с разбором ДЗ
Эксперт ответит на вопросы, объяснит сложные темы и поможет глубже понять материал.
Программа курса включает:
🔸 Разбор всех паттернов DDD
🔸 Value Object, Entity, Aggregate, Domain Service
🔸 Repository, Application Layer, HTTP/gRPC адаптеры
🔸 Kafka: входящие и исходящие адаптеры
🔸 Eventual Consistency, Outbox, Polling Publisher
📢 Записывайтесь сейчас и станьте экспертом в DDD и Clean Architecture на Go! 👉 https://microarch.ru/courses/ddd/languages/go?utm_source=posev&utm_medium=erid:2VtzqvFWyBX&utm_campaign=5
Реклама. ИП Ветчинкин К.Е. ИНН: 773376451099 Erid: 2VtzqvFWyBX
Если:
— код разваливается от правок,
— бизнес-логика размазана,
— Entity — просто структуры,
— а тесты живут своей жизнью —
вам точно сюда.
📦 Вы создадите сервис с нуля, разберете ключевые паттерны DDD и научитесь строить архитектуру, устойчивую к изменениям.
🔹 Как проходит обучение?
✅ Практика на реальном кейсе
Вы разработаете сервис диспетчеризации заказов, решая реальные бизнес-задачи.
✅ Видеоуроки в удобное время
Эксперт шаг за шагом создаст микросервис «Корзина», раскрывая ключевые паттерны DDD.
✅ Чат с экспертом
Не останетесь без ответов – задавайте вопросы в закрытом чате и получайте разбор сложных тем.
✅ Персональная обратная связь
Куратор проведет ревью кода, даст рекомендации и поможет разобраться с архитектурными решениями.
✅ Вебинары с разбором ДЗ
Эксперт ответит на вопросы, объяснит сложные темы и поможет глубже понять материал.
Программа курса включает:
🔸 Разбор всех паттернов DDD
🔸 Value Object, Entity, Aggregate, Domain Service
🔸 Repository, Application Layer, HTTP/gRPC адаптеры
🔸 Kafka: входящие и исходящие адаптеры
🔸 Eventual Consistency, Outbox, Polling Publisher
📢 Записывайтесь сейчас и станьте экспертом в DDD и Clean Architecture на Go! 👉 https://microarch.ru/courses/ddd/languages/go?utm_source=posev&utm_medium=erid:2VtzqvFWyBX&utm_campaign=5
Реклама. ИП Ветчинкин К.Е. ИНН: 773376451099 Erid: 2VtzqvFWyBX
🧠 Что выведет следующий код?
Варианты ответа:
A)
B)
C)
D)
Код выведет:
changed: 20
defer: 10
main: 20
Объяснение:
main: Переменная num инициализируется значением 10.
change(&num): Вызывается функция change, и ей передается адрес переменной num (указатель на num). Теперь параметр val внутри change указывает на ту же ячейку памяти, где хранится num.
defer fmt.Println("defer:", *val): Внутри change встречается оператор defer.
Важно: Аргументы для отложенного вызова (*val в данном случае) вычисляются сразу в момент объявления defer, а не в момент выполнения отложенной функции. На этом этапе *val (значение по адресу, на который указывает val, то есть значение num) равно 10. Таким образом, вызов fmt.Println("defer:", 10) ставится в очередь на выполнение перед выходом из функции change.
*val = 20: Значение по адресу, на который указывает val, изменяется на 20. Поскольку val указывает на num, это напрямую изменяет значение переменной num в функции main на 20.
fmt.Println("changed:", *val): Выводится текущее значение по адресу val, которое теперь равно 20. Печатается changed: 20.
Конец change: Функция change собирается завершиться. Перед выходом выполняется отложенный вызов.
Выполнение defer: Выполняется ранее запланированный вызов fmt.Println("defer:", 10). Печатается defer: 10.
Возврат в main: Управление возвращается в main.
fmt.Println("main:", num): Выводится текущее значение переменной num. Так как функция change изменила его через указатель, num теперь равно 20. Печатается main: 20.
@golangtests
package main
import (
"fmt"
)
func change(val *int) {
defer fmt.Println("defer:", *val)
*val = 20
fmt.Println("changed:", *val)
}
func main() {
num := 10
change(&num)
fmt.Println("main:", num)
}
Варианты ответа:
A)
defer: 20
main: 20
B)
defer: 10
main: 20
C)
changed: 20
main: 20
D)
defer: 20
main: 20
Код выведет:
defer: 10
main: 20
Объяснение:
main: Переменная num инициализируется значением 10.
change(&num): Вызывается функция change, и ей передается адрес переменной num (указатель на num). Теперь параметр val внутри change указывает на ту же ячейку памяти, где хранится num.
defer fmt.Println("defer:", *val): Внутри change встречается оператор defer.
Важно: Аргументы для отложенного вызова (*val в данном случае) вычисляются сразу в момент объявления defer, а не в момент выполнения отложенной функции. На этом этапе *val (значение по адресу, на который указывает val, то есть значение num) равно 10. Таким образом, вызов fmt.Println("defer:", 10) ставится в очередь на выполнение перед выходом из функции change.
*val = 20: Значение по адресу, на который указывает val, изменяется на 20. Поскольку val указывает на num, это напрямую изменяет значение переменной num в функции main на 20.
fmt.Println("changed:", *val): Выводится текущее значение по адресу val, которое теперь равно 20. Печатается changed: 20.
Конец change: Функция change собирается завершиться. Перед выходом выполняется отложенный вызов.
Выполнение defer: Выполняется ранее запланированный вызов fmt.Println("defer:", 10). Печатается defer: 10.
Возврат в main: Управление возвращается в main.
fmt.Println("main:", num): Выводится текущее значение переменной num. Так как функция change изменила его через указатель, num теперь равно 20. Печатается main: 20.
@golangtests
Highload буткемп: Системный дизайн, Производительность и Масштабирование
Приглашаем на курсы для прокачки навыков архитектора и проектировщика на весеннем highload-потоке от DevHands.
🌐 Приходите на уникальный образовательный трек без отрыва от работы (занятия вечером), в рамках которого вы:
🤩 изучите ключевые хайлоад-паттерны и получите навыки проектирования систем с миллионной аудиторией (балансировка, масштабирование, высокая доступность, шардинг, CAP/PACELS, транзакционные очереди и многое другое)
🤩 поупражняетесь в системном дизайне и проектировании, и получите живую обратную связь на реальных задачах: магазин/маркетплейс, объявления, соцсети, такси/доставка и тд.
🤩 погрузитесь в highload максимально, при желании в первый же день получите в управление свою инфраструктуру, попробуете “выжать” 100K RPS из своих сервисов, получите уникальный опыт работы с кластерными решениями - Redis, SPQR, CockroachDB
🤩 научитесь планировать нагрузку и связывать бизнес-показатели с нефункциональными требованиями к системе
🤩 попрактикуетесь в проведении и прохождения секций системного дизайна на интервью
Только «живые» онлайн-сессии: лекции, брейнштормы, презентации домашних проектов.
🗓 Старт потока 22 апреля, изучайте программу и записывайтесь:
🤩 Буткэмп “Производительность и масштабируемость” для тех, кто хочет поработать с собственной инфрой
🤩 Курс “Системный дизайн высоконагруженных проектов” для тех, кто хочет только практику проектирования “у доски”
🥸 Кто мы: R&D-центр Devhands, основатель и автор курса Алексей Рыбак, ex-СТО Badoo и Yum! Brands, член программного комитета Highload.
Реклама. ИП Рыбак А.А. ИНН 771407709607 Erid: 2VtzqwqhKdT
Приглашаем на курсы для прокачки навыков архитектора и проектировщика на весеннем highload-потоке от DevHands.
Только «живые» онлайн-сессии: лекции, брейнштормы, презентации домашних проектов.
Реклама. ИП Рыбак А.А. ИНН 771407709607 Erid: 2VtzqwqhKdT
Please open Telegram to view this post
VIEW IN TELEGRAM
Задача: Что выведет следующий код при компиляции и запуске с использованием Go 1.24?
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
count := 3
fmt.Println("Запуск горутин...")
wg.Add(count)
for i := 0; i < count; i++ {
// Запускаем горутину в каждой итерации
go func() {
defer wg.Done()
// Имитируем небольшую работу
time.Sleep(time.Duration(10) * time.Millisecond)
// Используем переменную цикла 'i' внутри горутины
fmt.Printf("Горутина видит i = %d\n", i)
}()
}
fmt.Println("Ожидание завершения горутин...")
wg.Wait()
fmt.Println("Все горутины завершены.")
}
Разбор:
Когда горутины реально начинали выполняться (после небольшой задержки time.Sleep), цикл for, скорее всего, уже завершался. К этому моменту переменная i имела бы свое конечное значение (в данном случае 3, так как цикл завершается, когда i становится равным count).
Поэтому в старых версиях Go этот код, скорее всего, вывел бы:
Запуск горутин...
Ожидание завершения горутин...
Горутина видит i = 3
Горутина видит i = 3
Горутина видит i = 3
Все горутины завершены.
(Порядок строк "Горутина видит..." мог быть разным). Чтобы обойти это, раньше приходилось делать так: i := i внутри цикла перед запуском горутины, чтобы создать копию переменной для каждой итерации.
Новое поведение (Go 1.22 и новее, включая 1.24): Начиная с Go 1.22, семантика переменных цикла for была изменена для предотвращения этой распространенной ошибки. Теперь переменная цикла (i в нашем случае) пересоздается для каждой итерации.
Это означает, что каждая горутина захватывает свою собственную копию i, соответствующую значению на момент этой итерации.
Ожидаемый вывод (Go 1.24): Благодаря изменению в Go 1.22, каждая горутина теперь корректно видит значение i той итерации, в которой она была запущена. Поэтому вывод будет (порядок строк "Горутина видит..." может варьироваться из-за недетерминированного планирования горутин):
Запуск горутин...
Ожидание завершения горутин...
Горутина видит i = 0
Горутина видит i = 1
Горутина видит i = 2
Все горутины завершены.
Use code with caution.
Или, например:
Запуск горутин...
Ожидание завершения горутин...
Горутина видит i = 2
Горутина видит i = 0
Горутина видит i = 1
Все горутины завершены.
Подвох заключается в том, что код выглядит как классический пример ошибки захвата переменной цикла, но из-за изменений в языке начиная с Go 1.22, он теперь работает "правильно" без необходимости явного копирования переменной (i := i).
Please open Telegram to view this post
VIEW IN TELEGRAM