> Можно ли передать функцию как аргумент в другую функцию в Go (Go)

Уровень: senior · Роль: backend · Категория: Технические вопросы

Компании: Avito

Стек: Go

> Пример ответа

Короткий ответ

Да, в Go функции - это значения первого класса, поэтому их можно передавать как аргументы в другие функции. Для этого используется тип функции, который описывает сигнатуру: параметры и возвращаемые значения. Например, func(int) int - это тип функции, принимающей int и возвращающей int. Такая возможность широко применяется для реализации callback-ов, middleware и паттернов вроде dependency injection.

Подробное объяснение

В Go функция объявляется через ключевое слово func, но сам тип функции - это отдельная сущность. Сигнатура функции (список параметров и результатов) определяет её тип. Две функции с одинаковой сигнатурой имеют одинаковый тип, даже если у них разные имена.

Передача функции как аргумента работает так же, как передача любого другого значения: при вызове функции-приёмника в неё копируется ссылка на функцию (в Go функции - это ссылочный тип, по сути указатель на код). Это позволяет:

  • вызывать переданную функцию внутри тела другой функции;
  • передавать несколько функций с разными сигнатурами;
  • возвращать функции из функций (замыкания);
  • хранить функции в структурах, слайсах, map.

Важно: тип функции должен быть известен на этапе компиляции. Нельзя передать "любую функцию" - всегда нужно указать конкретную сигнатуру. Для гибкости можно использовать интерфейсы или any, но тогда придётся делать type assertion, что менее безопасно.

На практике

На практике передача функций используется в трёх основных сценариях:

  1. Callback - функция вызывается после завершения операции (например, обработка результата HTTP-запроса, чтения из канала).
  2. Middleware - цепочка обработчиков, где каждый следующий получает функцию для вызова следующего шага (типично для HTTP-серверов, например, в net/http).
  3. Стратегия - передача алгоритма поведения (например, функция сортировки, функция сравнения в sort.Slice).

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

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

Пример кода

GO
package main
import "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 := 10
shifted := 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, и его нельзя передать как обычную функцию без обёртки.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью