Menu

Несколько возвращаемых значений в Golang: (value, error)

Функции в Go могут возвращать несколько значений. Здесь разобраны синтаксис, соглашение (value, error), идиома comma-ok, именованные результаты, голый return и пустой идентификатор.

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

Возврат нескольких значений

Перечислите типы результатов в скобках. Возвращайте значения через запятую и принимайте так же.

Встроенные min и max появились в Go 1.21. Вызывающий код обязан принять все результаты: x := minMax(nums) не компилируется с ошибкой assignment mismatch: 1 variable but minMax returns 2 values.

Соглашение (value, error)

Чаще всего два значения возвращают, чтобы сообщить об ошибке. По соглашению ошибка идёт последней, и если она не nil, остальные результаты считаются бессмысленными.

Вывод:

port: 8080
error: port "http": strconv.Atoi: parsing "http": invalid syntax
error: port out of range

При ошибке возвращайте нулевое значение для всех остальных результатов (0, "", nil). Полный шаблон, включая оборачивание и проверку, описан на странице обработка ошибок.

Идиома comma-ok

Несколько встроенных операций возвращают необязательное второе значение bool, которое говорит, настоящее ли первое значение. Переменную по соглашению называют ok.

Эти три формы (поиск в мапе, утверждение типа, получение из канала) особые: они возвращают одно или два значения в зависимости от того, скольким переменным вы присваиваете. Ваши собственные функции так не умеют. Функция, объявленная с двумя результатами, всегда возвращает два.

Пустой идентификатор

_ принимает значение и выбрасывает его. Используйте его для ненужных результатов:

_, err := fmt.Sscan(input, &n)
key, _ := parseLine(line)

Игнорирование ошибки через _ компилируется, поэтому делайте так, только если точно знаете, что вызов не может завершиться неудачей или неудача не важна. Например, strings.Builder.WriteString всегда возвращает nil-ошибку, а закрытие файла после записи нет.

Именованные результаты

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

Имена попадают в вывод go doc, что помогает, когда у двух результатов один тип: (quotient, remainder int) говорит больше, чем (int, int).

Голый return в длинной функции трудно читать: читателю приходится листать назад, чтобы понять, что лежит в результатах. Обычный совет: называйте результаты, когда имена что-то документируют, и всё равно пишите return quotient, remainder явно, если функция длиннее нескольких строк.

Именованные результаты и defer

Отложенная функция выполняется после того, как инструкция return записала результаты, и до того, как их получит вызывающий код. С именованными результатами отложенная функция может читать и менять их. Так добавляют контекст к любой ошибке, которую возвращает функция, или превращают панику в ошибку.

Программа печатает load : empty name, а затем <nil>. return errors.New(...) сначала присваивает значение err, а затем отложенная функция его оборачивает. Подробнее на странице о defer.

Затенение именованного результата

Именованные результаты живут во внешней области видимости функции. := во вложенном блоке создаёт новую переменную с тем же именем, и голый return вернёт внешнюю.

func find() (n int, err error) {
	if true {
		n, err := compute() // new n and err, shadowing the results
		_ = n
		_ = err
	}
	return // returns 0, nil
}

Худший случай компилятор ловит: голый return внутри блока, где результат затенён, даёт ошибку result parameter err not in scope at return. Вариант выше, где return стоит вне блока, он не ловит. Используйте = вместо :=, когда имеете в виду переменные результата.

Частые ошибки

  • Использование значения, когда err не nil. Сначала проверьте err. Функция вправе вернуть мусор вместе с ошибкой.
  • Возврат типизированного nil-указателя в качестве ошибки. var e *MyErr; return e возвращает не-nil error. При успехе возвращайте литерал nil.
  • Слишком много результатов. Три и больше результатов разного смысла неудобны в использовании. Верните структуру.

Часто задаваемые вопросы

Как вернуть несколько значений из функции в Go?

Перечислите типы результатов в скобках и верните значения через запятую: func minMax(xs []int) (int, int) { ...; return lo, hi }. Вызывающий код присваивает их все сразу: lo, hi := minMax(nums).

Как проигнорировать одно из возвращаемых значений в Go?

Присвойте его пустому идентификатору _: _, err := strconv.Atoi(s) или n, _ := strconv.Atoi(s). Совсем пропустить значение нельзя: число переменных слева должно совпадать с числом результатов.

Что такое именованные возвращаемые значения в Go?

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

Стоит ли использовать голый return в Go?

Только в коротких функциях. Голый return в длинной функции скрывает, что именно возвращается, и облегчает баги с затенением. Руководства по стилю Go советуют называть результаты ради документации, а в функциях длиннее нескольких строк возвращать значения явно.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ