#13.1 golang Функция как объект первого класса: Анонимные функции, колбэки, замыкания

Анонимная функция как тип данных

Мы можем:

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

Например, обычная функция определяется в отдельном блоке. У нее есть имя, в данном случае doNothing:

// обычная функция
func doNothing() {
   fmt.Println("Это обычная функция")
}

Анонимная функция не имеет имени и определяется там, где вы захотите, при этом имя функции не указывается, а сразу после ключевого слово func в скобках указываем входные параметры:

func main() {
	// анонимная функция
	func(in string) {
		fmt.Println("anon func out:", in)
	}("Привет!")
}

- здесь мы определили функцию и тут же вызвали её с аргументом "Привет!"

Присваивание переменной значения-функции

Также анонимную функцию можно присвоить какой-то переменной и потом вызвать ее. Мы будем обращаться к такой переменной как к функции, указывая для вызова скобки после имени переменной-функции:

func main() {
	printer := func(in string) {
		fmt.Println("printer outs:", in)
	}
	printer("Привет!")
}

Коллбэк и Определение отдельного типа данные для сигнатуры функции

Иногда вам приходится в нескольких местах объявлять функции, передавать их куда-то, поэтому можно определить специальный тип функции при помощи ключевого слова type:

// определяем тип функции:
//в данном случае принимаем один аргумент int и возвраем int
type myFunctionType func(int) int 

-- сигнатура функции тут выступает базовым типом для нашего myFunctionType.

Используем опеределенный выше тип, для описания типа коллбэка, который принимает функция и затем вызывает:

type myFunctionType func(int) int

func main() {
	// просто анонимная функция
	incFunction := func(value int) int {
		return value + 1
	}
	// используем пользовательский тип для параметра-колбэка
	worker := func(callback myFunctionType) {
		fmt.Println(callback(1))
	}
	worker(incFunction)
}

Замыкания

В golang можно создавать замыкания:

func main() {
	// функция возвращает замыкание
	fixedSummer := func(sumWithThis int) myFunctionType {
		return func(value int) int {
			// sumWithThis будет определено
			// через механизм замыкания из-за пределов
			// тела нашей анонимной функции
			return value + sumWithThis
		}
	}
	// получим замыкание над значением 5
	runner := fixedSummer(5)
	// выполним замкнутую функцию
	// -- она прибавит 5 к переданному агрументу (2)
	fmt.Println(runner(2)) // 7
}

-- в этом примере мы получаем sumWithThis внутри возвращаемой анонимной функции именно через механизм замыкания, захватывая это значение из-за пределов тела самой анонимной функции.

Из-за того, что golang имеет сборщик мусора переменная sumWithThis не удаляется сразу, а остается в памяти, пока мы храним ссылку на возвращенную анонимную функцию, которая в свою очередь ссылается на эту переменную.