A diferencia de Java, donde las funciones siempre pertenecen a una clase, en Go las funciones pueden ser independientes y no es necesario definirlas como métodos estáticos. Las funciones en Go son “ciudadanos de primera clase”: una función es un valor que se puede almacenar en una variable, pasar como argumento o devolver como resultado de otra función.
Una función puede tomar cero o más parámetros, y puede devolver cero o más valores.
1Parámetros¶
1.1Sintaxis básica¶
Los parámetros se declaran con nombre y tipo, separados por coma:
1 2 3func sumar(x int, y int) int { return x + y }
En este ejemplo, la función sumar toma dos parámetros de tipo int y devuelve un int. Cabe destacar que, a diferencia de Java, el tipo de retorno se escribe después de los paréntesis.
Si quisiéramos generar algo similar en Java deberíamos declarar una clase con métodos estáticos:
1 2 3 4 5public class Matemática { public static int sumar(int a, int b) { return a + b; } }
1.2Parámetros del mismo tipo¶
Cuando dos o más parámetros consecutivos comparten el mismo tipo, podemos declarar el tipo una sola vez al final:
1 2 3func sumar(x, y int) int { return x + y }
1.3Parámetros variádicos¶
Go permite definir funciones que aceptan una cantidad variable de argumentos con ... antes del tipo. Estos se conocen como parámetros variádicos y se reciben como un slice dentro de la función:
1 2 3 4 5 6 7func sumar(nums ...int) int { total := 0 for _, n := range nums { total += n } return total }
1 2 3 4 5 6import "fmt" func main() { fmt.Println(sumar(1, 2, 3)) fmt.Println(sumar(5, 10, 15, 20)) }
6
502Pasaje por valor¶
En Go los argumentos siempre se pasan por valor. Esto significa que la función recibe una copia del valor original y cualquier modificación dentro de la función no afecta a la variable que se pasó como argumento:
1 2 3func duplicar(x int) { x = x * 2 }
1 2 3a := 5 duplicar(a) fmt.Println(a)
5El valor de a sigue siendo 5 porque duplicar recibió una copia. Esto aplica a todos los tipos: int, float64, string, struct, arreglos, etc.
Los slices y los maps son casos especiales: la estructura que los representa (puntero, longitud, capacidad) se copia, pero tanto el original como la copia comparten el mismo arreglo subyacente. Esto se cubre en detalle en el próximo capítulo.
3Valores de retorno¶
3.1Retorno múltiple¶
Go permite devolver múltiples valores desde una función. Esto se usa frecuentemente para reportar errores junto con el resultado esperado:
1 2 3 4 5 6 7 8 9import "errors" func divisionSegura(dividendo, divisor float32) (float32, error) { if divisor == 0.0 { return 0.0, errors.New("división por cero") } return dividendo / divisor, nil }
Cuando se devuelven múltiples valores, los tipos se encierran entre paréntesis en el orden correspondiente.
3.2Valores de retorno nombrados¶
Go permite asignar nombres a los valores de retorno. Estos nombres actúan como variables declaradas dentro de la función, inicializadas con su valor cero. Al usar return sin argumentos se devuelven automáticamente los valores actuales de esas variables:
1 2 3 4 5 6 7 8 9func division(dividendo, divisor float32) (resultado float32, err error) { if divisor == 0.0 { err = errors.New("división por cero") return } resultado = dividendo / divisor return }
El retorno desnudo (return sin valores) es útil cuando los nombres de retorno mejoran la legibilidad, pero su uso excesivo puede tener el efecto contrario.
3.3Ignorar valores de retorno¶
Cuando una función devuelve múltiples valores y no necesitamos alguno de ellos, podemos ignorarlo con el identificador en blanco _:
1resultado, _ := divisionSegura(10, 2)
Esto evita tener que declarar variables que no se van a usar, lo que Go no permite.
4Funciones como valores¶
Al ser “ciudadanos de primera clase”, las funciones se pueden asignar a variables:
1 2f := sumar fmt.Println(f(3, 4))
7La variable f tiene tipo func(int, int) int. También podemos declarar el tipo explícitamente:
1 2 3var operacion func(int, int) int operacion = sumar fmt.Println(operacion(10, 5))
Esto permite pasar funciones como argumentos a otras funciones, lo que es la base de patrones como callbacks y funciones de orden superior.
5Funciones anónimas¶
Go soporta funciones sin nombre que se pueden definir en el lugar donde se necesitan:
1 2 3func() { fmt.Println("función anónima") }()
Los paréntesis al final invocan la función inmediatamente (IIFE). También se pueden asignar a una variable:
1 2 3 4 5saludar := func(nombre string) { fmt.Printf("Hola, %s\n", nombre) } saludar("Martín")
Hola, Martín6Ejercicios¶
Los ejercicios de este capítulo están en 04-funciones/ejercicios/
del repositorio taller-go.
Cada directorio contiene un README.md con el enunciado y los esqueletos
para resolverlo.