Programación · n.º 14 entre los lenguajes más usados

Aprende Go desde cero

Sencillo y rápido: servidores, herramientas de la nube y programas concurrentes.

  • 10 lecciones
  • 10 ejercicios interactivos
  • 19 conceptos clave
  • 16,5 % lo usa*

Qué es Go y para qué sirve

Go (o Golang) es un lenguaje creado en Google con una idea: que sea simple de leer y rápido de compilar. Genera un único ejecutable sin dependencias y trae de serie herramientas para la concurrencia. Docker y Kubernetes, por ejemplo, están escritos en Go.

  • Servidores y APIs
  • Herramientas de la nube y DevOps
  • Programas de terminal

Para empezar: Prueba los ejemplos en el Go Playground oficial o instala Go desde go.dev y ejecútalos con go run main.go.

Nivel 1 Básico

Lección 1 · Básico

Tu primer programa

Un programa de Go empieza en la función main del paquete main. Con import traes paquetes de la biblioteca estándar, como fmt, que escribe en la consola.

go run main.go compila y ejecuta en un paso; go build genera el ejecutable.

Ejemplo práctico
package main

import "fmt"

func main() {
	fmt.Println("¡Hola, mundo!")
	fmt.Println(2 + 3) // 5
}

Ejercicio

Completa la función que escribe una línea.

fmt.("Hola")
Ver la solución

Respuesta: Println. fmt.Println escribe sus argumentos y un salto de línea.

Lección 2 · Básico

Variables y tipos

:= declara una variable y deduce su tipo a la vez; var sirve para declararla con un tipo explícito. Los tipos básicos son int, float64, bool y string.

Una variable sin valor toma el «valor cero» de su tipo: 0, false o el texto vacío. Y si declaras una variable y no la usas, Go no compila.

Ejemplo práctico
package main

import "fmt"

func main() {
	nombre := "Ana"
	edad := 30
	var precio float64 = 19.99
	var activo bool // false (valor cero)

	fmt.Printf("%s tiene %d años. Total: %.2f. Activo: %t\n", nombre, edad, precio*3, activo)
}

Ejercicio

¿Qué hace edad := 30?

Ver la solución

Respuesta: Declara edad y deduce que es int. := declara una variable nueva e infiere su tipo a partir del valor.

Lección 3 · Básico

Condiciones, for y switch

Go solo tiene un bucle: for. Con tres partes es un contador clásico, con una condición funciona como un while y con range recorre colecciones.

if y switch no llevan paréntesis, pero las llaves son obligatorias. En switch no hace falta break.

Ejemplo práctico
package main

import "fmt"

func main() {
	for i := 1; i <= 3; i++ {
		fmt.Println("vuelta", i)
	}

	nota := 7
	switch {
	case nota >= 9:
		fmt.Println("Sobresaliente")
	case nota >= 5:
		fmt.Println("Aprobado")
	default:
		fmt.Println("Suspenso")
	}
}

Ejercicio

¿Cuántas palabras clave de bucle tiene Go?

Ver la solución

Respuesta: Una: for. Go solo tiene for, que cubre todos los casos.

Lección 4 · Básico

Slices y maps

Un slice es una lista de tamaño variable: []int{7, 8, 9}. append añade elementos y devuelve el slice nuevo, por eso se reasigna. len dice cuántos hay.

Un map guarda pares clave–valor. Al leer una clave puedes pedir un segundo valor que indica si existía.

Ejemplo práctico
package main

import "fmt"

func main() {
	notas := []int{7, 8, 9}
	notas = append(notas, 10)
	for i, n := range notas {
		fmt.Println(i, n)
	}

	stock := map[string]int{"teclado": 10, "raton": 0}
	if unidades, ok := stock["webcam"]; !ok {
		fmt.Println("No hay webcams en el catálogo", unidades)
	}
}

Ejercicio

Completa para añadir un 6 al slice.

notas = (notas, 6)
Ver la solución

Respuesta: append. append devuelve un slice con el elemento añadido; hay que guardarlo de nuevo en la variable.

Nivel 2 Intermedio

Lección 5 · Intermedio

Funciones y errores

Las funciones pueden devolver varios valores. Por convención, las que pueden fallar devuelven el resultado y un error, que vale nil si todo fue bien.

Go no tiene excepciones para los fallos normales: compruebas el error justo después de cada llamada.

Ejemplo práctico
package main

import (
	"errors"
	"fmt"
)

func dividir(a, b float64) (float64, error) {
	if b == 0 {
		return 0, errors.New("división entre cero")
	}
	return a / b, nil
}

func main() {
	if r, err := dividir(10, 4); err == nil {
		fmt.Println(r) // 2.5
	}
	if _, err := dividir(1, 0); err != nil {
		fmt.Println("Error:", err)
	}
}

Ejercicio

¿Qué valor tiene err cuando la función ha ido bien?

Ver la solución

Respuesta: nil. Un error igual a nil significa que no ha habido ningún problema.

Lección 6 · Intermedio

Structs y métodos

Un struct agrupa campos con nombre. Los métodos se declaran fuera, indicando antes del nombre a qué tipo pertenecen (el receptor).

Si el método modifica el struct, el receptor debe ser un puntero (*Cuenta).

Ejemplo práctico
package main

import "fmt"

type Cuenta struct {
	Titular string
	Saldo   float64
}

func (c *Cuenta) Ingresar(cantidad float64) {
	c.Saldo += cantidad
}

func main() {
	c := Cuenta{Titular: "Ana"}
	c.Ingresar(50)
	fmt.Printf("%s: %.2f\n", c.Titular, c.Saldo) // Ana: 50.00
}

Ejercicio

¿Por qué el receptor de Ingresar es *Cuenta y no Cuenta?

Ver la solución

Respuesta: Para modificar la cuenta original y no una copia. Con un receptor de valor, el método trabajaría sobre una copia y el cambio se perdería.

Lección 7 · Intermedio

Interfaces

Una interfaz es un conjunto de métodos. Cualquier tipo que tenga esos métodos la cumple automáticamente, sin escribir implements.

Así una función puede aceptar «cualquier cosa que sepa calcular su área», sea un círculo o un rectángulo.

Ejemplo práctico
package main

import (
	"fmt"
	"math"
)

type Figura interface {
	Area() float64
}

type Circulo struct{ Radio float64 }
type Rectangulo struct{ Ancho, Alto float64 }

func (c Circulo) Area() float64    { return math.Pi * c.Radio * c.Radio }
func (r Rectangulo) Area() float64 { return r.Ancho * r.Alto }

func main() {
	figuras := []Figura{Circulo{1}, Rectangulo{2, 3}}
	for _, f := range figuras {
		fmt.Printf("%.2f\n", f.Area())
	}
}

Ejercicio

¿Cómo indica un tipo de Go que cumple una interfaz?

Ver la solución

Respuesta: Basta con que tenga los métodos. Las interfaces de Go se cumplen de forma implícita: si tiene los métodos, la cumple.

Nivel 3 Avanzado

Lección 8 · Avanzado

Goroutines y canales

Una goroutine es una función que se ejecuta a la vez que el resto del programa; se lanza poniendo go delante de la llamada. Son muy ligeras: se pueden lanzar miles.

Los canales (chan) comunican goroutines de forma segura: una envía con <- y otra recibe. sync.WaitGroup espera a que terminen todas.

Ejemplo práctico
package main

import (
	"fmt"
	"sync"
)

func cuadrado(n int, resultados chan<- int, wg *sync.WaitGroup) {
	defer wg.Done()
	resultados <- n * n
}

func main() {
	resultados := make(chan int, 3)
	var wg sync.WaitGroup
	for _, n := range []int{2, 3, 4} {
		wg.Add(1)
		go cuadrado(n, resultados, &wg)
	}
	wg.Wait()
	close(resultados)

	suma := 0
	for r := range resultados {
		suma += r
	}
	fmt.Println(suma) // 29
}

Ejercicio

Completa para lanzar la función en una goroutine.

 descargar(url)
Ver la solución

Respuesta: go. Escribir go delante de una llamada la ejecuta en una goroutine nueva.

Lección 9 · Avanzado

Módulos y dependencias

Cada proyecto es un módulo, descrito en el archivo go.mod. go mod init lo crea y go get añade una dependencia.

go mod tidy limpia el archivo: añade lo que falta y quita lo que ya no usas. Las dependencias quedan fijadas con su versión exacta.

Ejemplo práctico
# En la terminal
go mod init ejemplo.com/tienda
go get github.com/google/uuid@latest
go mod tidy

// main.go
package main

import (
	"fmt"

	"github.com/google/uuid"
)

func main() {
	fmt.Println("Pedido", uuid.NewString())
}

Ejercicio

¿Qué archivo describe un módulo de Go y sus dependencias?

Ver la solución

Respuesta: go.mod. go.mod guarda el nombre del módulo, la versión de Go y las dependencias.

Lección 10 · Avanzado

Un servidor web en pocas líneas

La biblioteca estándar incluye net/http, suficiente para crear un servidor web o una API sin instalar nada más.

Cada ruta se asocia a una función que recibe la petición y escribe la respuesta. Desde Go 1.22 los patrones admiten el método y variables, como GET /saludo/{nombre}.

Ejemplo práctico
package main

import (
	"encoding/json"
	"log"
	"net/http"
)

func main() {
	http.HandleFunc("GET /saludo/{nombre}", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		json.NewEncoder(w).Encode(map[string]string{"saludo": "Hola, " + r.PathValue("nombre")})
	})
	log.Println("Escuchando en http://localhost:8080")
	log.Fatal(http.ListenAndServe(":8080", nil))
}

Ejercicio

¿Qué paquete de la biblioteca estándar sirve para crear un servidor web?

Ver la solución

Respuesta: net/http. net/http contiene el servidor y el cliente HTTP de Go.

¿Has terminado? Sigue practicando

Repasa los conceptos en el diccionario, practica con estas herramientas o prueba otro lenguaje: las ideas que has aprendido (variables, condiciones, funciones, objetos…) te servirán en cualquiera.

  • Calculadoras informáticas

    Calculadora de subredes IPv4

    Red, difusión, rango de hosts, máscara y wildcard de cualquier IP/CIDR, con división en subredes y VLSM.

  • Texto y programación

    Base64, URL y JSON

    Codifica y decodifica Base64 y URL, y valida o formatea JSON con la línea del error.

  • Texto y programación

    Conversor de fecha Unix y zonas horarias

    Pasa un timestamp Unix a fecha y al revés, y compara la hora en varias zonas horarias.

Fuentes consultadas

* Porcentaje de personas que dijeron haber usado Go en la Stack Overflow Developer Survey 2026: Technology (pregunta sobre lenguajes de programación, de scripts y de marcado; todas las respuestas). El progreso de las lecciones se guarda solo en este navegador.