Volver al blog

Palabras clave de Go: las 25 palabras reservadas explicadas

Go tiene 25 palabras clave, sin cambios desde Go 1.0. Mira qué hace cada una con un ejemplo real y qué nombres predeclarados, como any y len, no lo son.

Palabras clave de Go: las 25 palabras reservadas explicadas

Go (Golang) tiene 25 palabras clave: break, case, chan, const, continue, default, defer, else, fallthrough, for, func, go, goto, if, import, interface, map, package, range, return, select, struct, switch, type y var. Son palabras reservadas, así que no puedes usarlas como nombres de variables, funciones o tipos. La lista no ha cambiado desde Go 1.0 en 2012, y Go 1.27 sigue teniendo las mismas 25 (especificación de Go). Python tiene 35 palabras clave, Java tiene 51 y C++ tiene más de 80. La lista corta es intencionada. Go se diseñó para ser lo bastante pequeño como para que puedas tener el lenguaje entero en la cabeza.

Resumen rápido

  • Go tiene exactamente 25 palabras clave. La lista está en la especificación de Go y es la misma desde Go 1.0.
  • Se dividen en cuatro grupos. Declaraciones (package, import, const, var, type, func), tipos compuestos (struct, interface, map, chan), control de flujo (12 palabras clave, de if a return) y concurrencia y limpieza (go, select, defer).
  • Solo hay una palabra clave para bucles. for cubre los bucles con contador, los bucles tipo while, los bucles infinitos y, con range, la iteración sobre slices, maps, channels, enteros y funciones iteradoras.
  • any, int, string, error, nil, true, len y make no son palabras clave. Son identificadores predeclarados, así que puedes redeclararlos (shadowing), aunque normalmente no deberías.
  • Los genéricos no añadieron palabras clave. Go 1.18 añadió parámetros de tipo con nombres predeclarados (any, comparable) en lugar de nuevas palabras reservadas.

La lista completa de palabras clave de Go

Palabra claveCategoríaQué hace
packageDeclaraciónIndica el paquete al que pertenece un archivo
importDeclaraciónIncorpora otros paquetes
constDeclaraciónDeclara una constante de tiempo de compilación
varDeclaraciónDeclara una variable
typeDeclaraciónDeclara un tipo con nombre o un alias
funcDeclaraciónDeclara una función, un método o un literal de función
structTipo compuestoAgrupa campos con nombre en un solo tipo
interfaceTipo compuestoDeclara un conjunto de métodos (o una restricción de tipo)
mapTipo compuestoDeclara un tipo de tabla hash
chanTipo compuestoDeclara un tipo de channel
ifControl de flujoEjecuta un bloque cuando una condición es verdadera
elseControl de flujoEjecuta un bloque cuando la condición del if es falsa
forControl de flujoEl único bucle de Go
rangeControl de flujoItera sobre slices, maps, strings, channels, enteros e iteradores
switchControl de flujoElige una rama entre varias
caseControl de flujoUna rama de un switch o de un select
defaultControl de flujoLa rama que se ejecuta cuando ningún case coincide
fallthroughControl de flujoContinúa en el siguiente case de un switch
breakControl de flujoSale de un for, un switch o un select
continueControl de flujoSalta a la siguiente iteración del bucle
gotoControl de flujoSalta a una etiqueta dentro de la misma función
returnControl de flujoSale de una función, opcionalmente con valores
goConcurrenciaInicia una función en una nueva goroutine
selectConcurrenciaEspera sobre varias operaciones de channel
deferLimpiezaEjecuta una llamada cuando la función retorna

¿Cuáles son las palabras clave de declaración en Go?

Go tiene seis palabras clave de declaración, y este pequeño cargador de configuración las usa todas:

example.gogo
package main

import (
	"fmt"
	"time"
)

const defaultTimeout = 5 * time.Second

var maxRetries = 3

type Config struct {
	Addr    string
	Timeout time.Duration
}

func newConfig(addr string) Config {
	return Config{Addr: addr, Timeout: defaultTimeout}
}

func main() {
	cfg := newConfig(":8080")
	fmt.Println(cfg.Addr, cfg.Timeout, maxRetries)
}

package

Todo archivo de Go empieza con una cláusula package. Todos los archivos de un mismo directorio comparten el mismo nombre de paquete. El paquete llamado main es especial: se compila como ejecutable, y su función main es donde arranca el programa. Cualquier otro nombre genera una biblioteca que otros paquetes importan. La palabra clave package en Go explica en detalle las reglas de nombres, la visibilidad y los paquetes _test.

import

import hace que los nombres exportados de otro paquete estén disponibles en el archivo, como fmt.Println o http.ListenAndServe. Puedes agrupar los imports entre paréntesis, renombrar uno (yaml "gopkg.in/yaml.v3") o importar un paquete solo por sus efectos secundarios con _. Go se niega a compilar un archivo con un import sin usar, así que un archivo nunca lista un paquete que no usa. La palabra clave import en Go explica en detalle los alias, los imports en blanco y con punto, y los ciclos de importación.

const

const declara un valor fijado en tiempo de compilación, como un timeout o el nombre de una cabecera. Las constantes pueden no tener tipo. const maxBodyBytes = 1 << 20 no tiene tipo hasta que la usas, así que funciona como int o como int64 sin conversión. Dentro de un bloque const, el identificador predeclarado iota cuenta hacia arriba desde 0, y así es como Go escribe los enums. La palabra clave const en Go explica en detalle las constantes con y sin tipo, iota y los enums.

var

var declara una variable con un tipo explícito, un valor inicial o ambos. Toda variable empieza con el valor cero de su tipo: 0, "", false o nil. Dentro de las funciones, la mayor parte del código Go usa en su lugar la forma corta name := value. var sigue siendo la opción correcta a nivel de paquete y cuando quieres el valor cero a propósito, como en var buf bytes.Buffer. La palabra clave var en Go explica en detalle los valores cero, :=, el shadowing y las variables a nivel de paquete.

type

type declara un nuevo tipo con nombre, como un struct, una interface o una versión con nombre de un tipo existente, como type UserID int64. El nuevo tipo puede tener sus propios métodos. Con = declara en cambio un alias (type Handler = http.Handler), que es simplemente un segundo nombre para el mismo tipo. La palabra clave type en Go explica en detalle los tipos definidos, los alias, los métodos y los tipos genéricos.

func

func declara funciones y métodos, y también crea literales de función (closures). Un método tiene un receptor antes de su nombre: func (s *Server) Start() error. Las funciones de Go pueden devolver varios valores, y así es como se informan los errores: func parse(body []byte) (Request, error). La palabra clave func en Go explica en detalle los métodos, los closures, las funciones variádicas y las functional options.

¿Qué palabras clave de Go construyen tipos compuestos?

Cuatro palabras clave construyen tipos a partir de otros tipos. Los arrays, slices y punteros también son tipos compuestos, pero usan símbolos ([N]T, []T, *T) en lugar de palabras clave.

example.gogo
type Notifier interface {
	Notify(ctx context.Context, msg string) error
}

type SlackNotifier struct {
	Channel string
}

func (s SlackNotifier) Notify(ctx context.Context, msg string) error {
	fmt.Printf("#%s: %s\n", s.Channel, msg)
	return nil
}

func main() {
	var n Notifier = SlackNotifier{Channel: "alerts"}

	statusCounts := map[int]int{200: 41, 500: 2}
	events := make(chan string, 10)

	events <- fmt.Sprintf("%d server errors", statusCounts[500])
	n.Notify(context.Background(), <-events)
}

struct

struct agrupa campos con nombre en un solo tipo. Es lo que Go usa en lugar de las clases: añades métodos a un tipo struct y reutilizas comportamiento incrustando un struct dentro de otro en lugar de heredar de él. Los tags de struct como json:"email" indican a paquetes como encoding/json cómo codificar cada campo. La palabra clave struct en Go explica en detalle los tags de struct, los receptores por valor y por puntero, la incrustación y cómo comparar y copiar structs.

interface

interface declara un conjunto de métodos. Un tipo satisface una interface solo con tener esos métodos, sin ninguna declaración implements, así que SlackNotifier en el ejemplo anterior es un Notifier sin decirlo. Desde Go 1.18, las interfaces también describen restricciones de tipo para genéricos, como interface{ ~int | ~float64 }. La palabra clave interface en Go explica en detalle la satisfacción implícita, las interfaces nil, las aserciones de tipo y las restricciones.

map

map declara un tipo de tabla hash, escrito map[KeyType]ValueType. Creas uno con un literal o con make, y un map nil provoca un panic al escribir en él. Leer una clave que no existe devuelve el valor cero, y la forma de dos valores count, ok := statusCounts[404] te dice si la clave estaba. La especificación deja el orden de iteración sin definir, y el runtime lo aleatoriza a propósito. var vs make en Go explica por qué un map nil provoca un panic al escribir en él y un slice nil no. La palabra clave map en Go explica en detalle el idioma comma-ok, el orden de iteración, los conjuntos, el acceso concurrente y el paquete maps.

chan

chan declara un tipo de channel, el conducto que usan las goroutines para pasarse valores. chan<- Job es solo de envío y <-chan Job es solo de recepción, lo que permite que la firma de una función indique qué dirección usa. Los channels se crean con make, y un channel con buffer (make(chan string, 10)) guarda valores hasta que alguien los recibe. La palabra clave chan en Go explica en detalle los channels con y sin buffer, el cierre, los deadlocks y los patrones con select.

¿Qué palabras clave de Go controlan el flujo de un programa?

Doce palabras clave se encargan de las bifurcaciones y los bucles. Go no tiene while, do, try, catch ni el operador ternario. Los errores son valores de retorno normales que compruebas con if.

example.gogo
func parse(body []byte) (Request, error) {
	var req Request
	if err := json.Unmarshal(body, &req); err != nil {
		return Request{}, fmt.Errorf("decode request: %w", err)
	}
	if req.Email == "" {
		return Request{}, errors.New("email is required")
	}
	return req, nil
}

func countErrors(lines []string) int {
	errs := 0
	for _, line := range lines {
		if strings.HasPrefix(line, "#") {
			continue
		}
		if line == "EOF" {
			break
		}
		if strings.Contains(line, "level=error") {
			errs++
		}
	}
	return errs
}

if

if ejecuta un bloque cuando una condición es verdadera. La condición no necesita paréntesis, pero las llaves son obligatorias. Un if puede empezar con una sentencia corta, y la variable que declara solo existe dentro del if y de sus ramas else. El código Go usa esta forma constantemente, como en if err := json.Unmarshal(body, &req); err != nil. La palabra clave if en Go explica en detalle las condiciones que solo aceptan bool, las sentencias de inicialización, el shadowing, los retornos tempranos y la ausencia del operador ternario.

else

else se ejecuta cuando la condición del if es falsa, y else if encadena más condiciones. El Go idiomático usa else menos que la mayoría de los lenguajes. Cuando la rama del if termina en return, el resto de la función ya es el caso else, así que el código Go mantiene el camino feliz sin sangría y retorna pronto ante los errores. La palabra clave else en Go explica en detalle las cadenas else if, el ámbito de las variables del if, los retornos tempranos y la ausencia del operador ternario.

for

for es la única palabra clave de bucle en Go. for i := 0; i < n; i++ es el clásico bucle con contador, for queue.Len() > 0 es un bucle tipo while y for { ... } se repite para siempre hasta que algo lo interrumpe. Desde Go 1.22, en los módulos cuyo go.mod declara go 1.22 o posterior, cada iteración tiene su propia copia de la variable del bucle, así que las closures y las goroutines que se inician dentro de un bucle ya no comparten una única variable. La palabra clave for en Go explica en detalle las tres formas del bucle, range, las variables de bucle de Go 1.22, las etiquetas y los errores comunes con bucles.

range

range hace que un bucle for itere sobre una colección. Te da índice y valor en slices y arrays, clave y valor en maps, posición en bytes y rune en strings, y los valores recibidos en channels. Go 1.22 añadió el recorrido de un entero con range (for attempt := range 3 se ejecuta con 0, 1 y 2), y Go 1.23 añadió el recorrido de funciones iteradoras, como la que devuelve maps.Keys(m).

switch

switch elige una rama entre varias. A diferencia de C y Java, un switch de Go se detiene después del caso que coincide, así que nunca necesitas un break al final de cada caso. Un switch sin expresión funciona como una cadena if/else if más limpia, y un type switch (switch v := x.(type)) bifurca según el tipo dinámico de un valor de interface.

case

case marca una rama de un switch o de un select. Un solo case puede enumerar varios valores: case "GET", "HEAD":. En un switch, los casos se comprueban de arriba abajo, y gana la primera coincidencia.

default

default es la rama que se ejecuta cuando ningún case coincide. Puede aparecer en cualquier parte del switch, aunque por convención va al final. En un select, un caso default hace que toda la sentencia no bloquee: si ningún channel está listo, default se ejecuta de inmediato.

fallthrough

fallthrough hace que un switch continúe en el cuerpo del siguiente caso sin comprobar la condición de ese caso. Es poco frecuente en código real, y es el único punto en el que un switch de Go se comporta como en C. Los permisos por niveles son un caso en el que se lee bien:

example.gogo
func permissions(role string) []string {
	var perms []string
	switch role {
	case "admin":
		perms = append(perms, "delete")
		fallthrough
	case "editor":
		perms = append(perms, "write")
		fallthrough
	case "viewer":
		perms = append(perms, "read")
	default:
		return nil
	}
	return perms
}

permissions("editor") devuelve [write read] y permissions("admin") devuelve [delete write read]. fallthrough tiene que ser la última sentencia de un caso, y no está permitido en un type switch.

break

break sale del for, switch o select más interno. Esa última parte es una trampa habitual: un break dentro de un select solo sale del select, no del bucle que lo rodea. Para salir del bucle, ponle una etiqueta y escribe break loop:

example.gogo
func drain(ctx context.Context, events <-chan string) {
loop:
	for {
		select {
		case ev, ok := <-events:
			if !ok {
				break loop
			}
			fmt.Println("event:", ev)
		case <-ctx.Done():
			break loop
		}
	}
	fmt.Println("drained")
}

continue

continue se salta el resto de la iteración actual y empieza la siguiente. En countErrors, más arriba, se salta las líneas de comentario. Igual que break, admite una etiqueta para continuar un bucle exterior desde dentro de uno anidado.

goto

goto salta a una etiqueta dentro de la misma función. No puede saltar al interior de un bloque ni por encima de la declaración de una variable, lo que descarta la mayoría de los problemas que tiene goto en C. Rara vez lo escribirás. Aparece sobre todo en código generado y en algunas partes de bajo nivel de la biblioteca estándar. El propio backend de LevelUpGo tiene unas 30.000 líneas de Go sin contar tests ni datos de seed. Usa goto y fallthrough cero veces, frente a más de 250 bucles range y más de 40 llamadas a defer.

return

return termina una función y devuelve sus resultados. Las funciones de Go suelen devolver dos valores, un resultado y un error, y quien llama comprueba el error de inmediato. Con parámetros de resultado con nombre, un return sin valores devuelve sus valores actuales, pero la mayoría de las guías de estilo lo reservan para funciones cortas.

¿Qué palabras clave de Go gestionan la concurrencia y la limpieza?

Las tres últimas palabras clave son go, select y defer. Junto con chan, son lo que usa un servidor en Go para ejecutar trabajo en paralelo y limpiar después.

example.gogo
func startWorkers(ctx context.Context, n int, jobs <-chan string, results chan<- []byte) {
	for range n {
		go worker(ctx, jobs, results)
	}
}

func worker(ctx context.Context, jobs <-chan string, results chan<- []byte) {
	for {
		select {
		case url, ok := <-jobs:
			if !ok {
				return
			}
			body, err := fetch(ctx, url)
			if err != nil {
				log.Printf("fetch %s: %v", url, err)
				continue
			}
			results <- body
		case <-ctx.Done():
			return
		}
	}
}

func fetch(ctx context.Context, url string) ([]byte, error) {
	ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
	defer cancel()

	req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	return io.ReadAll(resp.Body)
}

go

go inicia una llamada a función en una nueva goroutine y continúa sin esperarla. startWorkers, más arriba, ejecuta go worker(ctx, jobs, results) en un bucle, lo que te da un pool de workers. Las goroutines empiezan con unos pocos kilobytes de pila, así que un servidor puede ejecutar cientos de miles de ellas. La sentencia go descarta los valores de retorno de la función, así que los resultados vuelven a través de channels, y un sync.WaitGroup o un errgroup espera a que termine el trabajo.

select

select espera sobre varias operaciones de channel y ejecuta la primera que esté lista. Si varias están listas a la vez, elige una al azar. El worker de arriba lo usa para tomar el siguiente trabajo o detenerse cuando se cancela el context. Combinar un channel con ctx.Done() o time.After es la forma en que el código Go añade timeouts y cancelación a cualquier cosa que bloquee.

defer

defer programa una llamada para que se ejecute cuando la función que la contiene retorna, tanto si retorna con normalidad como si hace panic. Mantiene la limpieza junto al código que la necesita: defer resp.Body.Close(), defer mu.Unlock(), defer cancel(). Las llamadas diferidas se ejecutan en orden LIFO (la última en entrar es la primera en salir), y sus argumentos se evalúan cuando se ejecuta la línea del defer, no al final. Un defer dentro de un bucle solo se ejecuta cuando retorna la función entera, y es uno de los errores comunes en Go que conviene conocer pronto.

¿Qué no son palabras clave en Go?

Muchos nombres que parecen integrados en el lenguaje son identificadores predeclarados, no palabras clave. La especificación de Go los enumera en el bloque universo, que rodea a todos los paquetes (especificación de Go):

TipoIdentificadores predeclarados
Tiposany, bool, byte, comparable, complex64, complex128, error, float32, float64, int, int8, int16, int32, int64, rune, string, uint, uint8, uint16, uint32, uint64, uintptr
Constantestrue, false, iota
Valor ceronil
Funcionesappend, cap, clear, close, complex, copy, delete, imag, len, make, max, min, new, panic, print, println, real, recover

La diferencia importa porque los identificadores predeclarados se pueden redeclarar en un ámbito interior. Esto compila:

example.gogo
func logAddrChange(cur, next Config) {
	old, new := cur.Addr, next.Addr
	if old != new {
		log.Printf("addr changed from %s to %s", old, new)
	}
}

func countLines(lines []string) int {
	len := len(lines)
	return len
}

Llamar new a una variable es habitual en código que compara valores antiguos y nuevos, y funciona. Sin embargo, después de len := len(lines), cualquier llamada posterior a len(...) en esa función falla con invalid operation: cannot call len (variable of type int): int is not a function. Una palabra clave no se puede usar así en absoluto. var := 3 falla con syntax error: unexpected :=, expected name. Linters como predeclared en golangci-lint señalan los built-ins redeclarados, porque un código como countLines compila pero confunde a quien lo lea después.

Mantener estos nombres fuera de la lista de palabras clave permite que Go crezca sin romper programas existentes. any (Go 1.18) y min, max y clear (Go 1.21) se añadieron como identificadores predeclarados, así que el código que ya usaba esos nombres como variables siguió compilando.

Dónde encaja LevelUpGo

LevelUpGo enseña Go con ejercicios que ejecutan código Go real en el navegador. El curso Go Basics cubre las palabras clave de declaración y de control de flujo, de var y const a switch y for. Concurrency Fundamentals cubre go, chan, select y defer, y Simplification muestra cuándo un switch se lee mejor que una cadena de if. El Training Ground tiene ejercicios cortos e independientes para practicarlas fuera de un curso.

Preguntas frecuentes

¿Cuántas palabras clave tiene Go?

Go tiene 25 palabras clave: break, case, chan, const, continue, default, defer, else, fallthrough, for, func, go, goto, if, import, interface, map, package, range, return, select, struct, switch, type y var. La lista está definida en la especificación de Go y no ha cambiado desde Go 1.0.

¿Es any una palabra clave en Go?

No. any es un identificador predeclarado, un alias de interface{} añadido en Go 1.18. Lo mismo ocurre con int, string, error, nil, true, false, iota y funciones integradas como len, make y append. Puedes redeclararlos en un ámbito interior, aunque los linters avisan cuando lo haces.

¿Puedo usar una palabra clave de Go como nombre de variable?

No. Las palabras clave están reservadas, así que var := 3 o func type() es un error de sintaxis. La solución habitual es una variante corta: el código Go suele usar typ o kind en lugar de type, pkg en lugar de package y fn en lugar de func.

¿Ha añadido Go palabras clave desde Go 1.0?

No. Las 25 palabras clave de Go 1.27 son las mismas 25 que traía Go 1.0 en marzo de 2012. Los genéricos, range sobre enteros, las funciones iteradoras y los métodos genéricos de Go 1.27 llegaron sin nuevas palabras clave. La promesa de compatibilidad de Go 1 hace improbables las nuevas palabras clave, porque una nueva palabra reservada rompería todos los programas que ya la usan como nombre.

¿Por qué Go tiene tan pocas palabras clave?

Los diseñadores lo quisieron así. El FAQ de Go dice que «intentaron reducir el desorden y la complejidad» y que la «sintaxis de Go es limpia y ligera en palabras clave». Por eso Go tiene una sola palabra clave de bucle en lugar de tres, y por eso los errores son valores de retorno en lugar de try/catch. El resultado es un lenguaje que se aprende rápido, y el código Go de distintos proyectos se lee de forma muy parecida.

Fuentes

Escribe Go como un ingeniero sénior

Lecciones interactivas en tu navegador. Las primeras son gratis.

Prueba una lección gratisO crea una cuenta gratuita