На этом шаге рассмотрим создание функций с несколькими необязательными аргументами различных типов.
В языке Go отсутствует непосредственная поддержка возможности создания функций с несколькими необязательными аргументами различных типов. Этого можно добиться с помощью специализированной структуры и того, что компилятор Go, всегда инициализирует переменные нулевыми значениями соответствующего типа.
Предположим, что имеется функция обработки некоторых пользовательских данных, которая по умолчанию обрабатывает все данные, но в иных ситуациях было бы желательно иметь возможность указывать первый и последний элементы для обработки, необходимость регистрации в журнале операций, выполняемых функцией, и передавать ей функцию обработки ошибок, возникающих при обработке недопустимых элементов.
Один из способов решения этой проблемы состоит в том, чтобы создать функцию с сигнатурой
ProcessItems(items Items, first, last int, audit bool, errorHandler func(item Item))
При такой организации значение 0 в параметре last будет означать, что индекс последнего элемента должен определяться автоматически, а функция errorHandler должна вызываться, только если она указана (то есть если этот параметр не равен nil). Это означает, что все вызовы, от которых требуется получить поведение по умолчанию, должны оформляться так:
ProcessItems(items, 0, 0, false, nil)
Более удачное решение заключается в том, чтобы определить функцию с сигнатурой
ProcessItems(items Items, options Options),
ProcessItems(items, Options{}).
А в отдельных случаях, при необходимости указать один или более дополнительных параметров, это можно было бы сделать, определив значения отдельных полей структуры Options.
Рассмотрим, как все вышесказанное выглядит в программном коде, начав с определения структуры Options.
type Options struct { First int // Первый элемент для обработки Last int // Последний элемент для обработки // (0 означает обрабатывать все, начиная с элемента First) Audit bool // Если true – регистрировать все операции ErrorHandler func(item Item) // Если не nil – вызывать для каждого // недопустимого элемента }
Структуры могут агрегировать или встраивать одно или более полей любых типов. Здесь структура Options агрегирует два поля типа int, поле типа bool и поле с функцией (ссылкой на функцию), имеющей сигнатуру func(Item), где Item – некоторый пользовательский тип (в данном случае – тип одного элемента в значении пользовательского типа Items).
ProcessItems(items, Options{}) errorHandler := func(item Item) { log.Println("Ошибка: ", item) } ProcessItems(items, Options{Audit: true, ErrorHandler: errorHandler})
В этом фрагменте демонстрируются два вызова пользовательской функции ProcessItems(). Первый вызов обрабатывает элементы с параметрами по умолчанию (то есть обрабатывает все элементы, не регистрирует операции и не вызывает обработчика ошибок для недопустимых элементов). Во втором вызове создается значение типа Options, в котором поля First и Last получают нулевые значения (тем самым функции предписывается обрабатывать все элементы), а поля Audit и ErrorHandler получают такие значения, что функция будет регистрировать в журнале все выполняемые ею операции и вызывать обработчик ошибок при обнаружении недопустимых элементов.
На следующем шаге рассмотрим функции init() и main().