Funciones en Go: múltiple retorno, named returns y funciones variádicas

Las funciones en Go son ciudadanas de primera clase: se pueden asignar a variables, pasar como argumentos y devolver como valores. Pero lo que diferencia a Go de otros lenguajes es el retorno múltiple integrado en la sintaxis, lo que convierte el patrón (valor, error) en el estándar de facto para manejar fallos sin excepciones.

Definición básica

package main

import "fmt"

func sumar(a, b int) int {
    return a + b
}

func main() {
    resultado := sumar(3, 4)
    fmt.Println(resultado) // 7
}

Cuando varios parámetros consecutivos comparten tipo, puedes escribirlo una sola vez: a, b int en vez de a int, b int.

Retorno múltiple

Go permite devolver más de un valor separando los tipos con comas:

func dividir(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("división por cero")
    }
    return a / b, nil
}

func main() {
    resultado, err := dividir(10, 3)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Printf("%.4fn", resultado) // 3.3333
}

El convenio en Go es devolver el error como último valor de retorno y comprobar siempre si es nil antes de usar el resultado. El compilador obliga a usar o ignorar explícitamente cada valor devuelto; si recibes un resultado y no lo usas, el programa no compila.

Named return values

Puedes dar nombre a los valores de retorno en la firma de la función. Se comportan como variables locales y un return sin argumentos los devuelve tal como están (naked return):

func estadisticas(nums []float64) (media, maxVal float64) {
    if len(nums) == 0 {
        return // devuelve 0, 0
    }
    total := 0.0
    maxVal = nums[0]
    for _, v := range nums {
        total += v
        if v > maxVal {
            maxVal = v
        }
    }
    media = total / float64(len(nums))
    return // naked return
}

func main() {
    m, mx := estadisticas([]float64{3, 7, 2, 9, 4})
    fmt.Printf("media=%.2f max=%.2fn", m, mx) // media=5.00 max=9.00
}

Los naked returns facilitan la documentación de la firma, pero en funciones largas pueden dificultar la lectura. Úsalos con moderación.

Funciones variádicas

El operador ... antes del tipo del último parámetro permite pasar un número variable de argumentos. Dentro de la función, el parámetro se comporta como un slice:

func suma(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

func main() {
    fmt.Println(suma(1, 2, 3))       // 6
    fmt.Println(suma(10, 20, 30, 40)) // 100
}

Si ya tienes un slice, puedes expandirlo con ... al llamar a la función:

numeros := []int{5, 10, 15}
fmt.Println(suma(numeros...)) // 30

Funciones como valores

Las funciones tienen tipo y se pueden asignar a variables o pasar como parámetros:

func aplicar(fn func(int) int, valor int) int {
    return fn(valor)
}

func doble(x int) int { return x * 2 }
func triple(x int) int { return x * 3 }

func main() {
    fmt.Println(aplicar(doble, 5))  // 10
    fmt.Println(aplicar(triple, 5)) // 15

    // función anónima directamente
    cuadrado := func(x int) int { return x * x }
    fmt.Println(aplicar(cuadrado, 4)) // 16
}

Closures

Una función anónima captura las variables del entorno que la rodea:

func contador() func() int {
    n := 0
    return func() int {
        n++
        return n
    }
}

func main() {
    c := contador()
    fmt.Println(c()) // 1
    fmt.Println(c()) // 2
    fmt.Println(c()) // 3

    c2 := contador() // instancia independiente
    fmt.Println(c2()) // 1
}

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP