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
}
