O Go (Golang) tem 25 palavras-chave: break, case, chan, const, continue, default, defer, else, fallthrough, for, func, go, goto, if, import, interface, map, package, range, return, select, struct, switch, type e var. São reservadas, por isso não as podes usar como nomes de variáveis, funções ou tipos. A lista não mudou desde o Go 1.0, em 2012, e o Go 1.27 continua a ter as mesmas 25 (especificação do Go). O Python tem 35 palavras-chave, o Java tem 51 e o C++ tem mais de 80. A lista curta é intencional. O Go foi desenhado para ser pequeno o suficiente para caber inteiro na tua cabeça.
Resumo rápido
- O Go tem exatamente 25 palavras-chave. A lista está na especificação do Go e é a mesma desde o Go 1.0.
- Dividem-se em quatro grupos. Declarações (
package,import,const,var,type,func), tipos compostos (struct,interface,map,chan), controlo de fluxo (12 palavras-chave, deifareturn) e concorrência e limpeza (go,select,defer). - Há apenas uma palavra-chave para ciclos. O
forcobre ciclos com contador, ciclos while, ciclos infinitos e, comrange, a iteração sobre slices, maps, channels, inteiros e funções iteradoras. any,int,string,error,nil,true,lenemakenão são palavras-chave. São identificadores predeclarados, por isso podes sombreá-los, embora normalmente não o devas fazer.- Os genéricos não acrescentaram palavras-chave. O Go 1.18 introduziu os parâmetros de tipo com nomes predeclarados (
any,comparable) em vez de novas palavras reservadas.
A lista completa das palavras-chave do Go
| Palavra-chave | Categoria | O que faz |
|---|---|---|
package | Declaração | Dá nome ao pacote a que o ficheiro pertence |
import | Declaração | Importa outros pacotes |
const | Declaração | Declara uma constante de tempo de compilação |
var | Declaração | Declara uma variável |
type | Declaração | Declara um tipo com nome ou um alias |
func | Declaração | Declara uma função, um método ou uma função literal |
struct | Tipo composto | Agrupa campos com nome num só tipo |
interface | Tipo composto | Declara um conjunto de métodos (ou uma restrição de tipo) |
map | Tipo composto | Declara um tipo de hash map |
chan | Tipo composto | Declara um tipo de channel |
if | Controlo de fluxo | Executa um bloco quando uma condição é verdadeira |
else | Controlo de fluxo | Executa um bloco quando a condição do if é falsa |
for | Controlo de fluxo | O único ciclo do Go |
range | Controlo de fluxo | Itera sobre slices, maps, strings, channels, inteiros e iteradores |
switch | Controlo de fluxo | Escolhe um ramo entre vários |
case | Controlo de fluxo | Um ramo de um switch ou de um select |
default | Controlo de fluxo | O ramo que corre quando nenhum case corresponde |
fallthrough | Controlo de fluxo | Continua para o case seguinte de um switch |
break | Controlo de fluxo | Sai de um for, switch ou select |
continue | Controlo de fluxo | Salta para a iteração seguinte do ciclo |
goto | Controlo de fluxo | Salta para uma etiqueta na mesma função |
return | Controlo de fluxo | Sai de uma função, opcionalmente com valores |
go | Concorrência | Inicia uma função numa nova goroutine |
select | Concorrência | Espera por várias operações em channels |
defer | Limpeza | Executa uma chamada quando a função retorna |
Quais são as palavras-chave de declaração do Go?
O Go tem seis palavras-chave de declaração, e este pequeno carregador de configuração usa todas:
example.gogopackage 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
Todos os ficheiros Go começam com uma cláusula package. Todos os ficheiros de um diretório partilham o mesmo nome de pacote. O pacote chamado main é especial: compila para um executável, e a sua função main é o ponto onde o programa arranca. Qualquer outro nome compila para uma biblioteca que outros pacotes importam. A palavra-chave package em Go aprofunda as regras de nomes, a visibilidade e os pacotes _test.
import
O import torna disponíveis no ficheiro os nomes exportados de outro pacote, como fmt.Println ou http.ListenAndServe. Podes agrupar os imports entre parênteses, dar outro nome a um (yaml "gopkg.in/yaml.v3") ou importar um pacote só pelos seus efeitos secundários com _. O Go recusa-se a compilar um ficheiro com um import não usado, por isso um ficheiro nunca lista um pacote que não usa. A palavra-chave import em Go aprofunda os aliases, os imports em branco e com ponto, e os ciclos de importação.
const
O const declara um valor fixo em tempo de compilação, como um timeout ou o nome de um header. As constantes podem não ter tipo. const maxBodyBytes = 1 << 20 não tem tipo até ser usada, por isso funciona como int ou como int64 sem conversão. Dentro de um bloco const, o identificador predeclarado iota conta a partir de 0, e é assim que o Go escreve enums. A palavra-chave const em Go aprofunda as constantes com e sem tipo, o iota e os enums.
var
O var declara uma variável com um tipo explícito, um valor inicial ou ambos. Todas as variáveis começam com o valor zero do seu tipo: 0, "", false ou nil. Dentro das funções, a maior parte do código Go usa antes a forma curta name := value. O var continua a ser a escolha certa ao nível do pacote e quando queres o valor zero de propósito, como em var buf bytes.Buffer. A palavra-chave var em Go aprofunda os valores zero, o :=, o shadowing e as variáveis ao nível do pacote.
type
O type declara um novo tipo com nome, como uma struct, uma interface ou uma versão com nome de um tipo existente, por exemplo type UserID int64. O novo tipo pode ter os seus próprios métodos. Com =, declara antes um alias (type Handler = http.Handler), que é apenas um segundo nome para o mesmo tipo. A palavra-chave type em Go aprofunda os tipos definidos, os aliases, os métodos e os tipos genéricos.
func
O func declara funções e métodos, e também cria funções literais (closures). Um método tem um recetor antes do nome: func (s *Server) Start() error. As funções em Go podem devolver vários valores, e é assim que os erros são reportados: func parse(body []byte) (Request, error). A palavra-chave func em Go aprofunda os métodos, as closures, as funções variádicas e as functional options.
Que palavras-chave do Go constroem tipos compostos?
Quatro palavras-chave constroem tipos a partir de outros tipos. Os arrays, slices e ponteiros também são tipos compostos, mas usam símbolos ([N]T, []T, *T) em vez de palavras-chave.
example.gogotype 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
O struct agrupa campos com nome num só tipo. É o substituto das classes no Go: associas métodos a um tipo struct e reutilizas comportamento incorporando uma struct noutra em vez de herdar dela. As struct tags como json:"email" dizem a pacotes como o encoding/json como codificar cada campo. A palavra-chave struct em Go aprofunda as struct tags, os receivers por valor e por ponteiro, a incorporação e como comparar e copiar structs.
interface
O interface declara um conjunto de métodos. Um tipo satisfaz uma interface só por ter esses métodos, sem nenhuma declaração implements, por isso o SlackNotifier acima é um Notifier sem o dizer. Desde o Go 1.18, as interfaces também descrevem restrições de tipo para genéricos, como interface{ ~int | ~float64 }. A palavra-chave interface em Go aprofunda a satisfação implícita, as interfaces nil, as asserções de tipo e as restrições.
map
O map declara um tipo de hash map, escrito map[KeyType]ValueType. Crias um com um literal ou com make, e um map nil entra em panic quando escreves nele. Ler uma chave inexistente devolve o valor zero, e a forma com dois valores count, ok := statusCounts[404] diz-te se a chave existia. A especificação deixa a ordem de iteração por definir, e o runtime torna-a aleatória de propósito. var vs make em Go explica porque é que um map nil entra em panic ao ser escrito e um slice nil não. A palavra-chave map em Go aprofunda o idioma comma-ok, a ordem de iteração, os conjuntos, o acesso concorrente e o pacote maps.
chan
O chan declara um tipo de channel, o canal que as goroutines usam para passar valores umas às outras. chan<- Job só envia e <-chan Job só recebe, o que permite à assinatura de uma função dizer que direção usa. Os channels são criados com make, e um channel com buffer (make(chan string, 10)) guarda valores até alguém os receber. A palavra-chave chan em Go aprofunda os channels com e sem buffer, o fecho, os deadlocks e os padrões com select.
Que palavras-chave do Go controlam o fluxo de um programa?
Doze palavras-chave tratam das ramificações e dos ciclos. O Go deixa de fora while, do, try, catch e o operador ternário. Os erros são valores de retorno comuns que verificas com if.
example.gogofunc 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
O if executa um bloco quando uma condição é verdadeira. A condição não precisa de parênteses, mas as chavetas são obrigatórias. Um if pode começar com uma instrução curta, e a variável que ela declara só existe dentro do if e dos seus ramos else. O código Go usa esta forma constantemente, como em if err := json.Unmarshal(body, &req); err != nil. A palavra-chave if em Go aprofunda as condições só com bool, as instruções de inicialização, o shadowing, os retornos antecipados e a ausência do operador ternário.
else
O else corre quando a condição do if é falsa, e o else if encadeia mais condições. O Go idiomático usa o else menos do que a maioria das linguagens. Quando o ramo do if termina em return, o resto da função já é o caso else, por isso o código Go mantém o caminho feliz sem indentação e retorna cedo em caso de erro. A palavra-chave else em Go aprofunda as cadeias else if, o âmbito das variáveis do if, os retornos antecipados e a ausência do operador ternário.
for
O for é a única palavra-chave de ciclo do Go. for i := 0; i < n; i++ é o clássico ciclo com contador, for queue.Len() > 0 é um ciclo while e for { ... } repete para sempre até alguma coisa sair dele. Desde o Go 1.22, em módulos cujo go.mod declara go 1.22 ou posterior, cada iteração recebe a sua própria cópia da variável do ciclo, por isso as closures e as goroutines iniciadas num ciclo deixaram de partilhar uma única variável. A palavra-chave for em Go aprofunda as três formas do ciclo, o range, as variáveis de ciclo do Go 1.22, as etiquetas e os erros comuns com ciclos.
range
O range faz um ciclo for iterar sobre uma coleção. Dá-te o índice e o valor em slices e arrays, a chave e o valor em maps, o offset em bytes e a rune em strings, e os valores recebidos em channels. O Go 1.22 acrescentou a iteração sobre um inteiro (for attempt := range 3 corre com 0, 1 e 2), e o Go 1.23 acrescentou a iteração sobre funções iteradoras, como a que maps.Keys(m) devolve.
switch
O switch escolhe um ramo entre vários. Ao contrário do C e do Java, um switch em Go para depois do case que corresponde, por isso nunca precisas de um break no fim de cada case. Um switch sem expressão funciona como uma cadeia if/else if mais limpa, e um type switch (switch v := x.(type)) ramifica conforme o tipo dinâmico de um valor de interface.
case
O case marca um ramo de um switch ou de um select. Um único case pode listar vários valores: case "GET", "HEAD":. Num switch, os cases são verificados de cima para baixo, e ganha a primeira correspondência.
default
O default é o ramo que corre quando nenhum case corresponde. Pode aparecer em qualquer ponto do switch, embora a convenção o ponha em último. Num select, um case default torna toda a instrução não bloqueante: se nenhum channel estiver pronto, o default corre de imediato.
fallthrough
O fallthrough faz um switch continuar para o corpo do case seguinte sem verificar a condição desse case. É raro em código real, e é o único sítio onde um switch em Go se comporta como em C. As permissões por níveis são um caso em que se lê bem:
example.gogofunc 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") devolve [write read] e permissions("admin") devolve [delete write read]. O fallthrough tem de ser a última instrução de um case, e não é permitido num type switch.
break
O break sai do for, switch ou select mais interior. Esta última parte é uma armadilha comum: um break dentro de um select só sai do select, não do ciclo à volta dele. Para sair do ciclo, põe-lhe uma etiqueta e escreve break loop:
example.gogofunc 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
O continue salta o resto da iteração atual e começa a seguinte. No countErrors acima, salta as linhas de comentário. Tal como o break, pode receber uma etiqueta para continuar um ciclo exterior a partir de um ciclo aninhado.
goto
O goto salta para uma etiqueta na mesma função. Não pode saltar para dentro de um bloco nem por cima de uma declaração de variável, o que elimina a maior parte dos problemas que o goto tem em C. Raramente o vais escrever. Aparece sobretudo em código gerado e em algumas partes de baixo nível da biblioteca padrão. O próprio backend do LevelUpGo tem cerca de 30.000 linhas de Go, sem contar testes e dados de seed. Usa goto e fallthrough zero vezes, contra mais de 250 ciclos range e mais de 40 chamadas defer.
return
O return termina uma função e devolve os seus resultados. As funções em Go devolvem muitas vezes dois valores, um resultado e um error, e quem chama verifica o erro de imediato. Com parâmetros de resultado com nome, um return sem argumentos devolve os valores atuais desses parâmetros, mas a maioria dos guias de estilo reserva isso para funções curtas.
Que palavras-chave do Go tratam da concorrência e da limpeza?
As três últimas palavras-chave são go, select e defer. Juntamente com chan, são o que um servidor em Go usa para executar trabalho em paralelo e limpar a seguir.
example.gogofunc 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
O go inicia uma chamada de função numa nova goroutine e continua sem esperar por ela. O startWorkers acima executa go worker(ctx, jobs, results) num ciclo, o que te dá uma worker pool. As goroutines começam com poucos kilobytes de stack, por isso um servidor consegue executar centenas de milhares delas. A instrução go descarta os valores de retorno da função, por isso os resultados voltam através de channels, e um sync.WaitGroup ou um errgroup espera que o trabalho termine.
select
O select espera por várias operações em channels e executa a primeira que estiver pronta. Se várias estiverem prontas ao mesmo tempo, escolhe uma ao acaso. O worker acima usa-o para pegar no próximo job ou parar quando o context é cancelado. Combinar um channel com ctx.Done() ou time.After é a forma como o código Go acrescenta timeouts e cancelamento a qualquer coisa que bloqueie.
defer
O defer agenda uma chamada para correr quando a função envolvente retorna, quer retorne normalmente, quer entre em panic. Mantém a limpeza junto do código que precisa dela: defer resp.Body.Close(), defer mu.Unlock(), defer cancel(). As chamadas diferidas correm por ordem inversa (a última a entrar é a primeira a sair), e os seus argumentos são avaliados quando a linha do defer corre, não no fim. Um defer dentro de um ciclo só corre quando a função inteira retorna, e esse é um dos erros comuns em Go que vale a pena conhecer cedo.
O que não são palavras-chave no Go?
Muitos nomes que parecem embutidos na linguagem são identificadores predeclarados, não palavras-chave. A especificação do Go lista-os no universe block, o bloco que envolve todos os pacotes (especificação do Go):
| Categoria | Identificadores predeclarados |
|---|---|
| Tipos | any, bool, byte, comparable, complex64, complex128, error, float32, float64, int, int8, int16, int32, int64, rune, string, uint, uint8, uint16, uint32, uint64, uintptr |
| Constantes | true, false, iota |
| Valor zero | nil |
| Funções | append, cap, clear, close, complex, copy, delete, imag, len, make, max, min, new, panic, print, println, real, recover |
A diferença importa porque os identificadores predeclarados podem ser redeclarados num âmbito interior. Isto compila:
example.gogofunc 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 }
Chamar new a uma variável é comum em código que compara valores antigos e novos, e funciona. Mas depois de len := len(lines), qualquer chamada len(...) posterior nessa função falha com invalid operation: cannot call len (variable of type int): int is not a function. Uma palavra-chave não pode ser usada assim de todo. var := 3 falha com syntax error: unexpected :=, expected name. Linters como o predeclared do golangci-lint assinalam built-ins sombreados, porque código como o countLines compila mas confunde quem o lê a seguir.
Manter estes nomes fora da lista de palavras-chave permite ao Go crescer sem partir programas existentes. O any (Go 1.18) e o min, o max e o clear (Go 1.21) foram acrescentados como identificadores predeclarados, por isso qualquer código que já usava esses nomes como variáveis continuou a compilar.
Onde entra o LevelUpGo
O LevelUpGo ensina Go através de exercícios que executam código Go real no navegador. O curso Go Basics cobre as palavras-chave de declaração e de controlo de fluxo, de var e const a switch e for. O Concurrency Fundamentals cobre go, chan, select e defer, e o Simplification mostra quando um switch se lê melhor do que uma cadeia de if. O Training Ground tem exercícios curtos e avulsos para as praticares fora de um curso.
Perguntas frequentes
Quantas palavras-chave tem o Go?
O Go tem 25 palavras-chave: break, case, chan, const, continue, default, defer, else, fallthrough, for, func, go, goto, if, import, interface, map, package, range, return, select, struct, switch, type e var. A lista está definida na especificação do Go e não mudou desde o Go 1.0.
O any é uma palavra-chave no Go?
Não. O any é um identificador predeclarado, um alias de interface{} acrescentado no Go 1.18. O mesmo vale para int, string, error, nil, true, false, iota e funções built-in como len, make e append. Podes redeclará-los num âmbito interior, embora os linters avisem quando o fazes.
Posso usar uma palavra-chave do Go como nome de variável?
Não. As palavras-chave são reservadas, por isso var := 3 ou func type() é um erro de sintaxe. A solução habitual é uma pequena variação: o código Go usa muitas vezes typ ou kind em vez de type, pkg em vez de package e fn em vez de func.
O Go acrescentou palavras-chave desde o Go 1.0?
Não. As 25 palavras-chave do Go 1.27 são as mesmas 25 que vieram com o Go 1.0, em março de 2012. Os genéricos, o range sobre inteiros, as funções iteradoras e os métodos genéricos no Go 1.27 chegaram todos sem novas palavras-chave. A promessa de compatibilidade do Go 1 torna improváveis novas palavras-chave, porque uma nova palavra reservada partiria todos os programas que já a usam como nome.
Porque é que o Go tem tão poucas palavras-chave?
Os criadores quiseram assim. A FAQ do Go diz que «tentaram reduzir a desordem e a complexidade» e que a «sintaxe é limpa e leve em palavras-chave» do Go. É por isso que o Go tem uma só palavra-chave de ciclo em vez de três, e que os erros são valores de retorno em vez de try/catch. O resultado é uma linguagem que se aprende depressa, e o código Go de bases de código diferentes lê-se de forma muito parecida.
Fontes
- The Go Programming Language Specification, Keywords: https://go.dev/ref/spec#Keywords
- The Go Programming Language Specification, Predeclared identifiers: https://go.dev/ref/spec#Predeclared_identifiers
- Go 1 and the Future of Go Programs (promessa de compatibilidade): https://go.dev/doc/go1compat
- Go FAQ, «What are the guiding principles in the design?»: https://go.dev/doc/faq#principles
- Notas de lançamento do Go 1.22 (variáveis de ciclo, range sobre inteiros): https://go.dev/doc/go1.22
- Notas de lançamento do Go 1.23 (range sobre funções iteradoras): https://go.dev/doc/go1.23
- Referência da linguagem Python, Keywords: https://docs.python.org/3/reference/lexical_analysis.html#keywords
- Java Language Specification (Java SE 21), Keywords: https://docs.oracle.com/javase/specs/jls/se21/html/jls-3.html#jls-3.9
- cppreference, C++ keywords: https://en.cppreference.com/w/cpp/keyword
