Несколько результатов
В прошлом уроке функция-прибор выдавала одно блюдо. Но бывают приборы с двумя выходами: соковыжималка за один запуск отдаёт и сок, и жмых.
Деление с остатком тоже даёт два числа — частное и остаток. Функция с одним результатом вернула бы только одно из них, и для второго понадобилась бы ещё одна функция. В Go функция возвращает сразу несколько значений:
package main
import "fmt"
func divmod(a, b int) (int, int) {
return a / b, a % b
}
func main() {
q, r := divmod(17, 5)
fmt.Println(q, r) // 3 2
}Список результатов
Давайте разберём пример по шагам:
(int, int)после параметров — список типов результатов в круглых скобках.return a / b, a % bвычисляет частное3и остаток2и возвращает оба.q, r := divmod(17, 5)присваивает вызов двум переменным:qполучает частное,r— остаток.fmt.Println(q, r)печатает3 2.
Списки должны совпадать в двух местах:
- значений после
returnстолько же, сколько типов в списке, и стоят они в том же порядке; - вызов присваивают такому же числу переменных.
Так функции в Go часто сообщают вместе с результатом, удалось ли действие. Многие функции стандартной библиотеки возвращают пару (T, error): значение какого-то типа T и ошибку.
Пустое имя _
Если вызов присваивают переменным, принять нужно каждый результат. Ненужное значение заменяют пустым именем _.
Запись q, _ := divmod(17, 5) сохраняет частное и отбрасывает остаток, как жмых, который сразу идёт в ведро. С обычной переменной вместо _ программа не скомпилировалась бы, если эту переменную потом не читать.
Здесь аналогия с соковыжималкой заканчивается. Жмых можно оставить в приборе и забыть о нём. В присваивании так нельзя: каждый результат нужно принять в переменную или явно выбросить через _.
Именованные результаты
Результатам можно дать имена: (lo, hi int). Представьте, что под выходами прибора заранее поставили подписанные миски.
Именованные результаты — это переменные, которые объявлены в начале функции. У них три свойства:
- они видны во всём теле функции;
- до первого присваивания в них лежат нулевые значения их типов: миски стоят пустые;
- имена подсказывают читателю, какой результат стоит первым.
Вернуть их значения можно двумя способами, и функция minMax показывает оба:
func minMax(a, b int) (lo, hi int) {
if a < b {
return a, b
}
lo = b
hi = a
return
}return со значениями
Инструкция return a, b работает так же, как в функции без имён. Она записывает a в lo, а b в hi и сразу их возвращает. В minMax эта ветка срабатывает, когда a уже меньше b.
Этот способ подходит для любой функции. В длинных функциях лучше перечислять значения явно: читателю не нужно искать, что лежит в мисках.
Голый return
Инструкция return без значений, голый return, возвращает текущие значения именованных результатов. Посмотрим на вызов:
lo, hi := minMax(8, 3)
fmt.Println(lo, hi) // 3 8Разберём вызов minMax(8, 3) по шагам:
- Условие
8 < 3ложно, поэтому функция идёт дальше. lo = bзаписывает3вlo.hi = aзаписывает8вhi.- Голый
returnотдаёт содержимое обеих мисок, и программа печатает3 8.
Голый return удобен в коротких функциях, где все миски видны с одного взгляда.
На собеседовании любят спрашивать, что вернёт голый return, если именованному результату ничего не присвоили. Ответ: нулевое значение его типа, например 0 для int. Голый return ничего не теряет — он отдаёт то, что лежит в мисках в эту секунду.
Частая ошибка
Вызов функции с двумя результатами присвоили одной переменной:
x := divmod(17, 5) // ✗
fmt.Println(x)assignment mismatch: 1 variable but divmod returns 2 valuesСлева переменных должно быть столько же, сколько результатов у функции. Пишите q, r := divmod(17, 5), а ненужное значение заменяйте на _.
Главное
- Функция может вернуть несколько значений: их типы перечисляют в скобках, а
returnотдаёт их через запятую. - В присваивании каждый результат принимают в переменную или отбрасывают через
_. - Именованные результаты — переменные с нулевыми значениями, а голый
returnвозвращает их текущие значения.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.