Именованные возвращаемые значения и naked return
В golang есть naked return и именованные возвращаемые значения. Зачем их придумали?
func c() (i int) {
return
}
Ответы (2 шт):
Предположим, у меня функция на экран или больше. Тогда оказывается неудобным читать её без наличия возвращаемых переменных.
func c() (i int) {
return
}
Если в коде функции возвращаемая переменная могла меняться, тогда становится некомфортно читать код. Следить за этим всем сложно.
Я её всегда старательно избегаю. Но часто вижу, как другие разработчики её используют. Несколько раз мне говорили, что используют эту конструкцию для сокращения кода. Только такой аргумент выглядит не очень разумным. Да и кода сокращается немного, а читаемость (опять же на мой взгляд) сильно падает.
При этом, следует отметить, что подход с именованными переменными хорош, так как позволяет указать, что за переменная возвращается. Подходу naked return он не противоречит:
func c() (i int) {
return i
}
Данный подход может использоваться для [go code review comments][1].
В некоторых случаях без named error не обойтись, но по-прежнему не ясно назначение naked return:
func noMorePanics() (err error) {
defer func() {
if r := recover(); r != nil {
err = r.(error)
}
}()
potentiallyPanickingFunction()
}
Но с использованием именованных возвращаемых значений следует быть крайне аккуратным, так как могут быть сайд-эффекты, которые сильно затрудняют чтение кода:
package main
import "fmt"
func main() {
f := fmt.Println
f(a())
f(b())
}
func a() int {
i := 0
defer func() {
i += 1
fmt.Println("a defer : ", i)
}()
return i
}
func b() (i int) {
i = 0
defer func() {
i += 1
fmt.Println("b defer : ", i)
}()
return i
}
Обращайте внимание на использование замыканий. Это может быть коварно. Во втором случае вернётся 1. А в первом 0. Это связано областью видимости и не интуитивно. i определяется внутри или снаружи функции. Но как мы видели выше, может быть полезно в специальных случаях. И быть выстрелом в ногу -- в других.
Я бы разделял именованные возвращаемые значения и return без значений.
Первая фича очень полезна, когда возвращаются больше одного значения. Имена служат документацией, особенно если несколько возвращаемых значений одного типа:
func Divmod64(numerator, denominator int64) (quotient, remainder int64) { ... }
Сразу понятно, где тут частное, а где остаток.
Named values return - это логическое продолжение идеи переменных для возвращаемых значений. Если мы их уже объявили в сингнатуре функции и присвоили в теле функции, то зачем повторно указывать в return-е?
Несмотря на то, что такой return позволяет сэкономить несколько символов при печати на клавиатуре, я предпочитаю всегда указывать, что именно я возвращаю. Однажды я потратил полдня на поиск ошибки, вызванной таким неявным возвратом значений. Больше не хочу. Целее буду :)