Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

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
3
func 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
5
public 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
3
func 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
7
func sumar(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}
1
2
3
4
5
6
import "fmt"

func main() {
    fmt.Println(sumar(1, 2, 3))
    fmt.Println(sumar(5, 10, 15, 20))
}
6
50

2Pasaje 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
3
func duplicar(x int) {
    x = x * 2
}
1
2
3
a := 5
duplicar(a)
fmt.Println(a)
5

El 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
9
import "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
9
func 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 _:

1
resultado, _ := 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
2
f := sumar
fmt.Println(f(3, 4))
7

La variable f tiene tipo func(int, int) int. También podemos declarar el tipo explícitamente:

1
2
3
var 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
3
func() {
    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
5
saludar := func(nombre string) {
    fmt.Printf("Hola, %s\n", nombre)
}

saludar("Martín")
Hola, Martín

6Ejercicios

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.