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.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR