Go: Функции

Теория: Анонимные функции

В языке Go функции можно объявлять не только с именем, но и без него. Такие функции называются анонимными. Они позволяют определять логику «на месте», без необходимости придумывать отдельное имя и выносить её в общий список функций. Это удобно в ситуациях, когда функция нужна только внутри ограниченного контекста — например, при работе с обработчиками событий, сортировкой, запуском горутин.

Представим: у нас есть программа, где надо один раз что-то посчитать. Создавать отдельную именованную функцию ради одной строчки — избыточно. В таких случаях анонимные функции помогают сделать код компактнее и читаемее.

Определение и вызов анонимной функции

Анонимная функция в Go объявляется так же, как и обычная, но без имени:

package main

import "fmt"

func main() {
	// Объявляем анонимную функцию и сразу её вызываем
	result := func(a, b int) int {
		return a + b
	}(3, 5)

	fmt.Println("Сумма:", result) // выведет: Сумма: 8
}

Здесь func(a, b int) int { return a + b } — это функция без имени. Мы сразу передали ей аргументы (3, 5) и получили результат.

Сохранение анонимной функции в переменную

Иногда удобнее сохранить функцию в переменной и использовать её несколько раз:

package main

import "fmt"

func main() {
	// Сохраняем анонимную функцию в переменную
	multiply := func(x, y int) int {
		return x * y
	}

	fmt.Println(multiply(2, 3)) // 6
	fmt.Println(multiply(4, 5)) // 20
}

Здесь multiply ведёт себя как обычная именованная функция, только её мы объявили внутри main.

Замыкания: доступ к внешним переменным

Анонимные функции могут обращаться к переменным, определённым снаружи. Это называется замыканием. Оно позволяет «захватывать» контекст, в котором функция создана.

package main

import "fmt"

func main() {
	counter := 0

	increment := func() int {
		counter++ // используем внешнюю переменную
		return counter
	}

	fmt.Println(increment()) // 1
	fmt.Println(increment()) // 2
	fmt.Println(increment()) // 3
}

Здесь increment «помнит» переменную counter, даже после первого вызова. Это делает анонимные функции удобными для создания функций-счётчиков, генераторов или обёрток.

Анонимные функции и горутины

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

package main

import (
	"fmt"
	"time"
)

func main() {
	go func() {
		fmt.Println("Привет из горутины!")
	}()

	time.Sleep(time.Second) // ждём, чтобы горутина успела выполниться
}

Мы не создавали отдельную функцию, а сразу передали анонимную в go.

Анонимные функции как аргументы других функций

Go активно применяет передачу функций как параметров. Благодаря анонимным функциям это можно делать прямо «на месте»:

package main

import (
	"fmt"
	"sort"
)

func main() {
	numbers := []int{5, 2, 9, 1, 3}

	// Используем анонимную функцию для сортировки
	sort.Slice(numbers, func(i, j int) bool {
		return numbers[i] < numbers[j]
	})

	fmt.Println(numbers) // [1 2 3 5 9]
}

Мы написали правило сортировки прямо в месте вызова sort.Slice, без создания отдельной функции.

Передача анонимных функций в другие функции

Go активно использует функции как параметры. Когда мы передаём анонимную функцию прямо «на месте», код остаётся компактным и читаемым.

Пример со стандартной библиотекой strings.Map, которая применяет функцию ко всем символам строки:

package main

import (
	"fmt"
	"strings"
)

func main() {
	text := "hello, world"

	// Анонимная функция для перевода букв в верхний регистр
	result := strings.Map(func(r rune) rune {
		if r >= 'a' && r <= 'z' {
			return r - ('a' - 'A')
		}

		return r
	}, text)

	fmt.Println(result) // HELLO, WORLD
}

Здесь мы описали логику прямо в месте вызова strings.Map. Если бы мы вынесли её в отдельную функцию, код стал бы длиннее и менее наглядным.

Возвращение анонимных функций (фабрики)

Функции могут возвращать другие функции. Если возвращаемая функция замыкает внешние переменные, мы получаем генератор поведения.

Пример: фабрика инкрементаторов.

package main

import "fmt"

// Функция возвращает анонимную функцию-счётчик

func newCounter(start int) func() int {
	count := start

	return func() int {
		count++
		return count
	}
}

func main() {
	counterA := newCounter(0)
	counterB := newCounter(100)

	fmt.Println(counterA()) // 1
	fmt.Println(counterA()) // 2
	fmt.Println(counterB()) // 101
	fmt.Println(counterB()) // 102
}

Каждый вызов newCounter создаёт своё замыкание с собственным count. В реальной жизни это часто используют для генерации идентификаторов, лимитеров и конфигурируемых обработчиков.

Анонимные функции внутри циклов и подводные камни

Очень часто разработчики запускают горутины внутри цикла. Но анонимные функции здесь могут привести к багу: они «захватывают» переменную цикла, а не её копию.

package main

import (
	"fmt"
	"time"
)

func main() {
	for i := 1; i <= 3; i++ {
		go func() {
			fmt.Println("Горутина:", i)
		}()
	}

	time.Sleep(time.Second)
}

Что выведется? Почти всегда три раза «Горутина: 4». Почему? Потому что все три анонимные функции замкнули одну и ту же переменную i, и к моменту выполнения горутин цикл уже завершился.

Как правильно:

for i := 1; i <= 3; i++ {
  go func(n int) {
    fmt.Println("Горутина:", n)
  }(i)
}

Теперь в замыкание передаётся копия i, и результат будет корректным: 1, 2, 3.

HTTP-обработчики

В веб-разработке на Go анонимные функции встречаются постоянно. В пакете net/http почти всегда используется именно этот подход:

package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Главная страница")
	})

	http.HandleFunc("/about", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "О проекте")
	})

	fmt.Println("Сервер запущен на :8080")
	http.ListenAndServe(":8080", nil)
}

Мы передали анонимные функции прямо в HandleFunc, и каждая из них знает, что делать с запросом. Такой приём позволяет держать логику рядом с маршрутизацией.

Отложенные вызовы (defer)

Анонимные функции удобны, когда нужно выполнить какой-то блок кода при выходе из функции.

package main

import "fmt"

func main() {
	fmt.Println("Начало работы")

	defer func() {
		fmt.Println("Очистка ресурсов перед завершением")
	}()

	fmt.Println("Основная логика программы")
}

Здесь defer гарантирует выполнение анонимной функции в самом конце работы main. Это хороший приём для освобождения ресурсов, закрытия файлов и соединений.

Выводы

Анонимные функции в Go позволяют писать компактный код, когда функция нужна только в ограниченном контексте. Они удобны для одноразовых вычислений, передачи функций как аргументов, работы с горутинами и создания замыканий. Главная их сила — доступ к внешнему окружению и возможность держать логику «рядом с использованием».

Рекомендуемые программы

+7 800 100 22 47

бесплатно по РФ

+7 495 085 21 62

бесплатно по Москве

108813 г. Москва, вн.тер.г. поселение Московский,
г. Московский, ул. Солнечная, д. 3А, стр. 1, помещ. 20Б/3
ОГРН 1217300010476
ИНН 7325174845