> Можно ли передать функцию как аргумент в другую функцию в Go (Go)
Уровень: senior · Роль: backend · Категория: Технические вопросы
Компании: Avito
Стек: Go
> Пример ответа
Короткий ответ
Да, в Go функции - это значения первого класса, поэтому их можно передавать как аргументы в другие функции. Для этого используется тип функции, который описывает сигнатуру: параметры и возвращаемые значения. Например, func(int) int - это тип функции, принимающей int и возвращающей int. Такая возможность широко применяется для реализации callback-ов, middleware и паттернов вроде dependency injection.
Подробное объяснение
В Go функция объявляется через ключевое слово func, но сам тип функции - это отдельная сущность. Сигнатура функции (список параметров и результатов) определяет её тип. Две функции с одинаковой сигнатурой имеют одинаковый тип, даже если у них разные имена.
Передача функции как аргумента работает так же, как передача любого другого значения: при вызове функции-приёмника в неё копируется ссылка на функцию (в Go функции - это ссылочный тип, по сути указатель на код). Это позволяет:
- вызывать переданную функцию внутри тела другой функции;
- передавать несколько функций с разными сигнатурами;
- возвращать функции из функций (замыкания);
- хранить функции в структурах, слайсах, map.
Важно: тип функции должен быть известен на этапе компиляции. Нельзя передать "любую функцию" - всегда нужно указать конкретную сигнатуру. Для гибкости можно использовать интерфейсы или any, но тогда придётся делать type assertion, что менее безопасно.
На практике
На практике передача функций используется в трёх основных сценариях:
- Callback - функция вызывается после завершения операции (например, обработка результата HTTP-запроса, чтения из канала).
- Middleware - цепочка обработчиков, где каждый следующий получает функцию для вызова следующего шага (типично для HTTP-серверов, например, в
net/http). - Стратегия - передача алгоритма поведения (например, функция сортировки, функция сравнения в
sort.Slice).
Часто функции передаются вместе с замыканиями - это позволяет захватывать внешние переменные и сохранять состояние между вызовами. При этом нужно помнить про возможные гонки данных, если замыкание использует общие переменные из нескольких горутин.
Ещё один важный момент - производительность. Передача функции как аргумента не создаёт накладных расходов на копирование кода, только на передачу указателя. Однако вызов функции через переменную (не напрямую) может препятствовать инлайнингу, что в горячих циклах может быть заметно.
Пример кода
GOpackage mainimport "fmt"// apply принимает функцию f и вызывает её для каждого элемента слайсаfunc apply(nums []int, f func(int) int) []int {result := make([]int, len(nums))for i, n := range nums {result[i] = f(n)}return result}// double - обычная функция, подходящая по сигнатуреfunc double(x int) int {return x * 2}func main() {nums := []int{1, 2, 3}// передаём именованную функциюdoubled := apply(nums, double)fmt.Println(doubled) // [2 4 6]// передаём анонимную функцию (замыкание)offset := 10shifted := apply(nums, func(x int) int {return x + offset})fmt.Println(shifted) // [11 12 13]}
Как отвечать на собеседовании
Начни с прямого ответа "да", затем кратко объясни, что функции - это значения первого класса. Покажи, что понимаешь разницу между объявлением функции и её типом. Приведи пример с сигнатурой и передачей в другую функцию. Упомяни типичные сценарии: callback, middleware, стратегия.
Если спросят про ограничения - скажи, что тип должен быть известен на этапе компиляции, и что для произвольных функций нужен any с type assertion. Если спросят про производительность - упомяни про возможное препятствие инлайнингу.
Хорошо добавить пример с замыканием, чтобы показать, что функция может захватывать внешние переменные. Это часто ценят интервьюеры, так как демонстрирует понимание области видимости.
Что проверяет интервьюер
Интервьюер проверяет:
- базовое понимание типов в Go, в частности функциональных типов;
- умение работать с функциями как с значениями;
- знание практических паттернов (callback, middleware);
- понимание замыканий и их поведения;
- осознание ограничений (статическая типизация, type assertion);
- возможно, знание про производительность и инлайнинг.
На уровне senior важно не просто сказать "да", а показать, где это применяется в реальной разработке, и какие есть подводные камни. Также оценивается способность объяснить сложные вещи простыми словами.
Типичные ошибки
- Путаница между объявлением функции и её типом - например, попытка передать функцию без указания сигнатуры.
- Использование
anyвместо конкретного типа - это приводит к потере типобезопасности и необходимости type assertion. - Забывают про замыкания - не упоминают, что анонимные функции могут захватывать внешние переменные.
- Неправильное понимание порядка выполнения - например, думают, что функция передаётся "по значению" и копируется код, хотя копируется только ссылка.
- Игнорирование возможных гонок данных - если замыкание использует общие переменные из нескольких горутин.
- Передача метода вместо функции - метод имеет сигнатуру с receiver, и его нельзя передать как обычную функцию без обёртки.
> Похожие задачи по backend
Какова асимптотическая сложность доступа по ключу в хэш-таблице
Какова структура HTTP-ответа
Как использовать EXPLAIN и ANALYZE для оптимизации SQL запросов
В чем разница между Kafka и RabbitMQ
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью