Как в Go передать функцию в качестве аргумента в другую функцию? (Функции высшего порядка)

Ответ

В Go функции являются гражданами первого класса (first-class citizens). Это означает, что их можно присваивать переменным, передавать в качестве аргументов другим функциям и возвращать из них. Функции, которые принимают другие функции в качестве аргументов или возвращают их, называются функциями высшего порядка (higher-order functions).

Способы передачи функции как параметра

Существует два основных способа это сделать:

1. Прямое указание сигнатуры функции

Вы можете напрямую в списке аргументов указать тип функции, которую ожидаете получить.

package main

import "fmt"

// process принимает число и функцию, которая будет это число обрабатывать.
// Тип функции-аргумента: func(int) int (принимает int, возвращает int)
func process(value int, action func(int) int) int {
    fmt.Printf("Выполняем действие над числом %dn", value)
    return action(value)
}

// double - конкретная функция, соответствующая сигнатуре func(int) int
func double(n int) int {
    return n * 2
}

func main() {
    // Передаем именованную функцию 'double'
    result1 := process(5, double)
    fmt.Printf("Результат: %dnn", result1) // Результат: 10

    // Передаем анонимную функцию прямо в вызове
    result2 := process(5, func(n int) int {
        return n * n
    })
    fmt.Printf("Результат: %dn", result2) // Результат: 25
}

2. Использование именованного типа функции

Для улучшения читаемости и переиспользования сложных сигнатур можно объявить собственный тип функции с помощью ключевого слова type.

package main

import "fmt"

// объявляем собственный тип для функции, которая принимает и возвращает int
type IntOperation func(int) int

// applyOperation использует наш новый тип для большей ясности
func applyOperation(value int, op IntOperation) int {
    return op(value)
}

func increment(n int) int {
    return n + 1
}

func decrement(n int) int {
    return n - 1
}

func main() {
    // Теперь мы можем передавать любые функции, 
    // которые соответствуют сигнатуре IntOperation
    fmt.Println(applyOperation(10, increment)) // 11
    fmt.Println(applyOperation(10, decrement)) // 9
}

Практическое применение

  • Middleware в веб-серверах: Обработчики HTTP-запросов часто оборачиваются в цепочку функций (middleware) для логирования, аутентификации, сжатия и т.д.
  • Callbacks: Для выполнения кода после завершения асинхронной операции.
  • Реализация паттерна "Стратегия": Возможность подменять алгоритм выполнения на лету.
  • Обработка данных: Написание универсальных функций для работы со слайсами (например, filter, map), которые принимают функцию-предикат.