Перейти к содержимому
Теория · Функции · урок 3 из 10

Несколько результатов

В прошлом уроке функция-прибор выдавала одно блюдо. Но бывают приборы с двумя выходами: соковыжималка за один запуск отдаёт и сок, и жмых.

Деление с остатком тоже даёт два числа — частное и остаток. Функция с одним результатом вернула бы только одно из них, и для второго понадобилась бы ещё одна функция. В Go функция возвращает сразу несколько значений:

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
}

Список результатов

Давайте разберём пример по шагам:

  1. (int, int) после параметров — список типов результатов в круглых скобках.
  2. return a / b, a % b вычисляет частное 3 и остаток 2 и возвращает оба.
  3. q, r := divmod(17, 5) присваивает вызов двум переменным: q получает частное, r — остаток.
  4. fmt.Println(q, r) печатает 3 2.

Списки должны совпадать в двух местах:

  • значений после return столько же, сколько типов в списке, и стоят они в том же порядке;
  • вызов присваивают такому же числу переменных.

Так функции в Go часто сообщают вместе с результатом, удалось ли действие. Многие функции стандартной библиотеки возвращают пару (T, error): значение какого-то типа T и ошибку.

Пустое имя _

Если вызов присваивают переменным, принять нужно каждый результат. Ненужное значение заменяют пустым именем _.

Запись q, _ := divmod(17, 5) сохраняет частное и отбрасывает остаток, как жмых, который сразу идёт в ведро. С обычной переменной вместо _ программа не скомпилировалась бы, если эту переменную потом не читать.

Здесь аналогия с соковыжималкой заканчивается. Жмых можно оставить в приборе и забыть о нём. В присваивании так нельзя: каждый результат нужно принять в переменную или явно выбросить через _.

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

Результатам можно дать имена: (lo, hi int). Представьте, что под выходами прибора заранее поставили подписанные миски.

Именованные результаты — это переменные, которые объявлены в начале функции. У них три свойства:

  • они видны во всём теле функции;
  • до первого присваивания в них лежат нулевые значения их типов: миски стоят пустые;
  • имена подсказывают читателю, какой результат стоит первым.

Вернуть их значения можно двумя способами, и функция minMax показывает оба:

GO
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, возвращает текущие значения именованных результатов. Посмотрим на вызов:

GO
lo, hi := minMax(8, 3)
fmt.Println(lo, hi) // 3 8

Разберём вызов minMax(8, 3) по шагам:

  1. Условие 8 < 3 ложно, поэтому функция идёт дальше.
  2. lo = b записывает 3 в lo.
  3. hi = a записывает 8 в hi.
  4. Голый return отдаёт содержимое обеих мисок, и программа печатает 3 8.

Голый return удобен в коротких функциях, где все миски видны с одного взгляда.

На собеседовании любят спрашивать, что вернёт голый return, если именованному результату ничего не присвоили. Ответ: нулевое значение его типа, например 0 для int. Голый return ничего не теряет — он отдаёт то, что лежит в мисках в эту секунду.

Частая ошибка

Вызов функции с двумя результатами присвоили одной переменной:

GO
x := divmod(17, 5) // ✗
fmt.Println(x)
TEXT
assignment mismatch: 1 variable but divmod returns 2 values

Слева переменных должно быть столько же, сколько результатов у функции. Пишите q, r := divmod(17, 5), а ненужное значение заменяйте на _.

Главное

  • Функция может вернуть несколько значений: их типы перечисляют в скобках, а return отдаёт их через запятую.
  • В присваивании каждый результат принимают в переменную или отбрасывают через _.
  • Именованные результаты — переменные с нулевыми значениями, а голый return возвращает их текущие значения.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом