#13.0 golang Функции - и работа с ними, вариативные функции, именнованые результаты, количество аргументов, распаковка слайса

Функция в Go объявляется через служебное слово func:

package main

import (
	"fmt"
)

func main() {
	fmt.Println(returnMe(1))
}

// обычное объявление
func returnMe(value int) int {
	return value + 1
}

-- для возврата значения используем return, аргументы перечисляются в скобках.

Если у нас несколько параметров одного типа, идущие подряд, то можно не указыватьтип для каждого параметра:

// много параметров
func getSum(a, b int, c int) int {
   return a + b + c
}

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

В Go можно сразу указывать переменную, в которую будет возвращён результат.
При этом вы можете записать в нее значение и написать пустой return без имени переменной. Она вернет то, что вы записали:

// именованный результат
func namedReturn() (out int) {
   out = 2
   return
}

-- в данном случае это переменная out. Впрочем, можно написать, например, return 3. Даже в таком случае функция будет корректно работать.

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

Функции могут возвращать и несколько значений сразу.
Часто это используется в т.ч. для возврата ошибки в качестве второго параметра:

func main() {
	a, b := multipleReturn(1)
	fmt.Println(a, b) // напечает 1 nil
}

// несколько результатов
func multipleReturn(in int) (int, error) {
	if in > 2 {
		// нулевой результат и ошибка для пробленой ситуации
		return 0, fmt.Errorf("some error happend")
	}
	return in, nil // результат и пустое значение вместо ошибки
}

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

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

Возрат нескольких именованных результатов

Мы можем везвращать из функции несколько именованных результатов, для этого достаточно объявить их имена:

// несколько именованных результатов
func multipleNamedReturn(ok bool) (rez int, err error) {
   rez = 1
   if ok {
      err = fmt.Errorf("some error happend")
      // return
      // аналогично return rez, err
      return 3, fmt.Errorf("some error happend")
   }
   rez = 2
   return
}

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

ПРИМЕЧАНИЕ: Несмотря на наличие переменных для именованных результатов , вовсе не обязательно использовать в return именно их. Мы вправе вернуть самостоятельно, например, 3 и ошибку:

func main() {
	a, b := multipleNamedReturn(true)
	fmt.Println(a, b) // напечает 1 nil
}

func multipleNamedReturn(ok bool) (rez int, err error) {
	rez = 1
	if ok {
		err = fmt.Errorf("some error happend")
		// return
		// аналогично return rez, err
		return 3, fmt.Errorf("some error happend")
	}
	rez = 2
	// игнорируем специальные переменные:
	return 3, fmt.Errorf("some new error happend")
}

Вариантивные функции - любое число аргументов одного типа

Также имеется возможность принимать неограниченное количество параметров, но только одного типа, для чего перед типом параметра указывается троеточие:

func main() {
	fmt.Println(sum(2, 3, 5, 7))
}

// нефиксированное количество параметров
func sum(numbers ...int) (result int) {
	fmt.Printf("in := %#v \n", numbers) // []int{2, 3, 5, 7}
	for _, val := range numbers {
		result += val
	}
	return
}

-- такие функции называются вариативными (англ. variadic).
В последнем пример в переменной numbers оказывается срез целых чисел, по которым вы сможете проитерироваться, используя цикл for range.

Отметим, что функция fmt.Println из наших примеров как раз является примером вариативной функции. Она принимает множество параметров и выводит их в строковое представление.

Распаковка слайса/среза

В связи с вариантивными функциями обсудим распаковку слайса, чтобы передать слайс в такую такую фукцию его нужно "распаковать" с помощью оператора троечия после имени переменной:

func main() {
	myNumbers := []int{2, 3, 5, 7}
	fmt.Println(sum(myNumbers...)) // передача с распаковкой
}

func sum(numbers ...int) (result int) {
	fmt.Printf("in := %#v \n", numbers) // []int{2, 3, 5, 7}
	for _, val := range numbers {
		result += val
	}
	return
}

Видео-материалы

Key Words for FKN + antitotal forum (CS VSU):

vedro-compota's picture

разобрать невозможность вернуть nil вместо простого типа

_____________
матфак вгу и остальная классика =)

vedro-compota's picture

return 0, fmt.Errorf("some error happend") - пояснить тип

_____________
матфак вгу и остальная классика =)