Menu

Funções em Golang: declarar, chamar e passar funções

Como declarar e chamar funções em Go: parâmetros, parâmetros que compartilham o tipo, valores de retorno, passagem por valor e funções como valores que você guarda e repassa.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Declarando uma função

Uma declaração de função é func, um nome, os parâmetros, o tipo do resultado e um corpo. O tipo vem depois do nome do parâmetro, não antes.

add recebe dois valores int e devolve um int. greet não devolve nada, então a assinatura não tem tipo de resultado e o corpo não precisa de return.

Uma função que declara um resultado precisa terminar em uma instrução de encerramento. Se algum caminho puder chegar à chave de fechamento sem retornar, o compilador para com missing return.

Parâmetros que compartilham o tipo

Parâmetros consecutivos do mesmo tipo podem compartilhar um único nome de tipo. func add(a, b int) int significa exatamente o mesmo que func add(a int, b int) int. Misturar também é permitido:

func scale(x, y float64, factor int) (float64, float64)

Aqui x e y são float64 e factor é um int.

Valores de retorno

Uma função pode devolver zero, um ou vários valores. Vários resultados vão entre parênteses, e quem chama os recebe com uma atribuição múltipla:

Isto imprime 3 2. O par (valor, error) é o uso mais comum desse recurso em Go, e ele tem uma página própria sobre múltiplos valores de retorno.

Argumentos são copiados

Go passa todo argumento por valor. A função trabalha em uma cópia, então atribuir a um parâmetro nunca muda a variável de quem chama.

A saída é:

{1 2}
{100 2}
[99 2 3]

A terceira linha surpreende. Um valor de slice é um cabeçalho pequeno (ponteiro, tamanho, capacidade). A cópia aponta para o mesmo array subjacente, então escrever em s[0] escreve no elemento de quem chama. Mas um append dentro da função pode alocar um array novo, e quem chama nunca vê esse cabeçalho novo. Se uma função aumenta um slice, devolva o slice novo. A mesma lógica vale para maps: a função pode acrescentar chaves que quem chama enxerga, porque o valor do map aponta para dados compartilhados.

Use um parâmetro ponteiro quando a função precisar modificar a variável de quem chama, ou quando o valor for uma struct grande que você não quer copiar a cada chamada.

Funções são valores

Uma função tem um tipo, escrito sem o nome: func(int, int) int. Você pode guardar uma função em uma variável, colocá-la em um map ou slice e passá-la para outra função.

square é uma função anônima atribuída a uma variável. Funções anônimas também podem ler e alterar variáveis do escopo ao redor, o que as torna closures.

Um tipo de função nomeado deixa as assinaturas mais legíveis quando o mesmo formato aparece em muitos lugares:

type Transform func(int) int

func apply(xs []int, f Transform) []int

O valor zero de um tipo de função é nil. Chamar uma função nil causa panic com invalid memory address or nil pointer dereference, então verifique callbacks opcionais antes de chamá-los. Valores de função só podem ser comparados com nil, nunca entre si.

Recursão

Uma função pode chamar a si mesma. Go não tem otimização de chamada de cauda, mas as pilhas das goroutines crescem sob demanda, então profundidades comuns de recursão não são problema.

for i := range 6 conta de 0 a 5 e exige o Go 1.22 ou mais recente.

O que as funções em Go não têm

  • Sem sobrecarga. Duas funções no mesmo pacote não podem ter o mesmo nome, nem com parâmetros diferentes.
  • Sem argumentos padrão ou nomeados. Todo parâmetro é passado em toda chamada, na ordem. Uma struct de configuração dá campos nomeados e opcionais: NewServer(Config{Port: 8080}).
  • Sem funções nomeadas aninhadas. Dentro do corpo de uma função você só pode declarar funções anônimas, normalmente atribuídas a uma variável.

Listas de argumentos de tamanho variável são suportadas por meio de funções variádicas.

Funções exportadas e não exportadas

Uma função cujo nome começa com letra maiúscula (Parse) é exportada e pode ser chamada de outros pacotes. Um nome com minúscula (parse) só é visível dentro do próprio pacote. Não existem as palavras-chave public ou private; a letra inicial é a regra inteira.

Erros comuns

  • Esperar que a mudança em um parâmetro chegue a quem chama. Atribuir a um parâmetro só muda a cópia. Devolva o valor novo ou receba um ponteiro.
  • Fazer append dentro de uma função e ignorar o resultado. func add(s []int) { s = append(s, 1) } não afeta o tamanho do slice de quem chama. Escreva func add(s []int) []int e use s = add(s).
  • Resultados ignorados. Go permite ignorar um valor de retorno. O go vet não aponta um erro descartado, mas linters como o errcheck (parte do golangci-lint) apontam. Use _ = só quando ignorar for uma decisão.

Perguntas frequentes

Como definir uma função em Go?

Use a palavra-chave func, um nome, uma lista de parâmetros com os tipos depois dos nomes e o tipo do resultado: func add(a, b int) int { return a + b }. Uma função sem resultado omite o tipo, e uma função com vários resultados os lista entre parênteses: func divmod(a, b int) (int, int).

Go passa argumentos por valor ou por referência?

Sempre por valor. A função recebe uma cópia de cada argumento. Para um int ou uma struct, essa cópia é independente da variável de quem chama. Slices, maps, channels e ponteiros são valores pequenos que apontam para dados compartilhados, então mudanças nos elementos apontados ficam visíveis para quem chama, enquanto reatribuir o próprio parâmetro não fica. Passe um ponteiro (*T) quando a função precisar mudar a variável de quem chama.

Dá para passar uma função como parâmetro em Go?

Sim. Funções são valores com um tipo, como func(int) int. Declare um parâmetro desse tipo e passe qualquer função com assinatura compatível, nomeada ou anônima: func apply(xs []int, f func(int) int).

Go tem sobrecarga de funções ou parâmetros padrão?

Não. Cada nome de função em um pacote precisa ser único, e todo parâmetro precisa ser passado em toda chamada. Os substitutos comuns são nomes distintos (NewServer, NewServerWithTLS), uma struct de configuração, parâmetros variádicos ou functional options.

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

COMEÇAR