Desenvolvimento Back-end Go

Trabalhando com funções na Golang

Neste artigo vamos aprender sobre as funções na Golang. Veremos o que são, qual a sua importância em nossos programas e como utilizá-las.

há 1 ano 4 meses


Você sabia que a TreinaWeb é a mais completa escola para desenvolvedores do mercado?

O que você encontrará aqui na TreinaWeb?

Conheça os nossos cursos

Durante o desenvolvimento de nossos programas, é muito comum encontrarmos a necessidade de repetir trechos de código, como, por exemplo, imagine que precisamos realizar a validação de um CPF informado pelo usuário em diferentes locais do nosso código. Simplesmente repetir esse trecho de código pode gerar muitos problemas, pois, talvez seja necessário alterar como a validação será realizada, o que exigirá que alteremos esse trecho de código em vários locais diferentes, gerando bastante trabalho para realizar essas alterações.

Tendo em vista esse problema, uma maneira simples de resolvê-lo é, isolar essa regra de validação que se repete em uma função ou procedimento para podermos reutilizá-la sempre que necessário. Além disso, caso seja necessário alterar como tal validação é realizada, precisaremos alterar em um único local do nosso código.

Assim sendo, nesse artigo vamos aprender como podemos criar funções na Golang, possibilitando assim, termos um código mais fácil de ser mantido ao longo de sua vida útil.

Go Básico
Curso Go Básico
Conhecer o curso

O que é uma função?

Simplificando, uma função é uma seção de código que, uma vez definida, podemos reutilizar. Funções são utilizadas para conseguirmos tornar o nosso código mais fácil de entender e manter através da quebra do nosso programa em pequenas tarefas reaproveitáveis.

Mesmo que essa seja sua primeira vez ouvindo falar do conceito de funções, se você já leu outros artigos sobre a linguagem Go aqui no blog da TreinaWeb, com certeza já utilizou alguma função disponibilizada pela própria linguagem. Um bom exemplo disso é a função Println do pacote fmt.

Por que utilizar funções?

Qualquer linguagem de programação considera as funções uma parte importante, pois elas permitem organizar o código em pequenas unidades modulares e reutilizáveis. Assim sendo, as funções nos proporcionam uma série de benefícios, como:

  • As funções permitem que você divida seu código em blocos lógicos e independentes, o que torna a legibilidade e a facilidade de manutenção melhores. Isso ocorre porque os blocos são mais fáceis de ler e entender. Isso torna seu código mais fácil de manter, pois você pode atualizar e modificar as funções individuais sem afetar o restante do código.
  • Reutilização de código. As funções permitem que você reutilize o mesmo código em vários lugares, o que pode economizar seu tempo e tornar seu código mais eficiente. Isso é especialmente útil quando você precisa executar a mesma operação várias vezes ou quando precisa compartilhar o mesmo código com outros desenvolvedores.
  • Melhor organização. As funções permitem que você agrupe códigos relacionados, tornando seu código mais organizado e fácil de gerenciar. Isso pode ajudar a evitar a duplicação e reduzir a complexidade geral do seu código.
  • Maior confiabilidade e tratamento de erros. As funções permitem que você teste e depure seu código com mais facilidade, porque você pode testar cada função de forma independente e identificar erros mais rapidamente. Isso pode ajudá-lo a escrever um código mais confiável e robusto.

Sintaxe das funções na Golang

Primeiramente, para podermos criar nossas próprias funções na Golang, precisamos entender qual a sintaxe utilizada para realizar a definição de uma função.

Para criarmos uma função utilizamos a palavra reservada func. Uma função em Go e na maioria das linguagens de programação é composta por quatro partes, sendo elas:

  • Nome: o nome da função, esse nome é o que iremos utilizar para podermos executar essa função posteriormente ao longo do nosso código;
  • Parâmetros: os parâmetros são onde definimos quais são os dados que precisam ser passados para que a nossa função possa funcionar corretamente, podemos passar mais de um parâmetro, desde que esses parâmetros estejam separados por vírgula;
  • Retorno: no retorno nós definimos qual será o tipo de dado retornado pela nossa função ao término do seu processamento;
  • Corpo: o corpo da função é onde iremos colocar o código que irá realizar o processamento da função, ou seja, é onde colocamos o código que iremos reaproveitar.

Além disso, devemos estar cientes que todas as partes informadas acima, com exceção do corpo, são opcionais. Logo, podemos criar uma função que não possua um nome, não receba parâmetros e que também não retorne nada ao final de sua execução.

func validarCpf(cpf string) bool {
	// Condigo onde teremos a validação do cpf
}

No código acima nós temos a definição de uma função chamada validarCpf, essa função recebe um parâmetro chamado cpf que deve ser do tipo string e ao término de sua execução irá retornar um dado do tipo bool.

Funções sem parâmetro e sem retorno

Agora vamos falar sobre alguns tipos de funções na Golang, o primeiro tipo de função que iremos falar são as funções sem parâmetro e sem retorno. Sempre que escrevemos um programa em Go nós utilizamos esse tipo de função, pois sempre precisamos definir a função main.

Porém, fora do caso da função main esse tipo de função não é muito comum de ser utilizada, pois possuem seu funcionamento estático, ou seja, independente de quando ou como sejam executadas sempre irão produzir o mesmo resultado.

package main

import "fmt"

func boasVindas() {
	fmt.Println("Boas-vindas ao meu programa escrito em Go!")
}

func main() {
	boasVindas()
}

Veja que no exemplo acima nós temos duas funções sem parâmetro e sem retorno, são as funções main e boasVindas. Na função boasVindas temos o comando para exibir no terminal o texto “Boas-vindas ao meu programa escrito em Go!”. Enquanto na nossa função main nós temos a execução da função boasVindas, para podermos executar uma função basta colocarmos o nome da função seguida da abertura e fechamento de parênteses.

Quando o código acima for executado, teremos o seguinte resultado no terminal:

Boas-vindas ao meu programa escrito em Go!

Também é possível realizar a execução da função boasVindas quantas vezes quisemos, bastando chamá-la quantas vezes forem necessárias.

package main

import "fmt"

func boasVindas() {
	fmt.Println("Boas-vindas ao meu programa escrito em Go!")
}

func main() {
	boasVindas()
	boasVindas()
	boasVindas()
	boasVindas()
}

Agora nós executamos a função boasVindas quatro vezes, a execução do código acima irá resultar no seguinte:

Boas-vindas ao meu programa escrito em Go!
Boas-vindas ao meu programa escrito em Go!
Boas-vindas ao meu programa escrito em Go!
Boas-vindas ao meu programa escrito em Go!

Funções com parâmetro

C# (C Sharp) Básico
Curso C# (C Sharp) Básico
Conhecer o curso

Apesar do nosso exemplo de função sem parâmetro e sem retorno funcionar bem, ela não é muito útil, pois sempre irá produzir o mesmo resultado. Podemos tornar a nossa função mais interessante a partir do uso de parâmetros.

Como dito anteriormente, os parâmetros de uma função são os dados que uma função precisa receber para possa funcionar corretamente. Para o nosso exemplo atual, podemos receber uma string, que seria o nome de alguma pessoa que desejamos dar boas-vindas.

Os parâmetros de uma função são definidos entre os parênteses dessa função e podem ser acessados em seu corpo como se fossem simples variáveis.

package main

import "fmt"

func boasVindas(nome string) {
	fmt.Println("Boas-vindas", nome)
}

func main() {
	boasVindas("Cleyson")
}

Veja que agora nossa função boasVindas recebe um parâmetro chamado nome do tipo string. Esse parâmetro nome está sendo utilizado para podermos alterar o comportamento de nossa função, fazendo assim que a mensagem impressa no terminal difira conforme o valor do parâmetro passado.

Ao executarmos o exemplo acima, iremos obter o seguinte resultado no terminal:

Boas-vindas Cleyson

É importante frisar que, uma vez que o ou os parâmetros de uma função sejam definidos, eles se tornam obrigatórios, ou seja, caso tentemos executar essa função sem passar nenhum parâmetro iremos obter um erro de compilação.

package main

import "fmt"

func boasVindas(nome string) {
	fmt.Println("Boas-vindas", nome)
}

func main() {
	boasVindas()
}

A execução do código acima irá resultar no seguinte erro:

# command-line-arguments
./main.go:10:2: not enough arguments in call to boasVindas
        have ()
        want (string)

Veja que o compilador da Go informa a seguinte mensagem “not enough arguments in call to boasVindas”, que traduzindo, fica, “argumentos insuficientes na chamada para boasVindas”.

Por fim, uma função pode receber quantos parâmetros sejam necessários, desde que, eles sejam separados por vírgula.

package main

import "fmt"

func somar(x int, y int) {
	resultado := x + y
	fmt.Println(resultado)
}

func main() {
	somar(1, 2)
}

Funções com retorno

Além de receber parâmetros, uma função também pode retornar um ou mais valores. Para isso, basta definirmos o tipo de retorno da função, o tipo do retorno deve ser definido após a lista de parâmetros.

Uma particularidade das funções na Golang é que elas podem retornar mais de um valor, isso não é comum em outras linguagens de programação, mas é uma característica muito interessante da linguagem Go.

Retornando um único valor

Para retornar um único valor, basta definirmos o tipo do retorno após a lista de parâmetros.

package main

import "fmt"

func somar(x int, y int) int {
  resultado := x + y
  return resultado
}

func main() {
  resultado := somar(1, 2)
  fmt.Println(resultado)
}

No exemplo acima, a função somar recebe dois parâmetros do tipo int e retorna um valor do tipo int. Ao chamarmos a função somar podemos armazenar o valor retornado em uma variável.

A execução do código acima irá resultar no seguinte:

3

Retornando mais de um valor

Para retornar mais de um valor, basta definirmos os tipos dos retornos após a lista de parâmetros, separando-os por vírgula.

package main

import "fmt"

func somarESubtrair(x int, y int) (int, int) {
  soma := x + y
  subtracao := x - y
  return soma, subtracao
}

func main() {
  soma, subtracao := somarESubtrair(1, 2)
  fmt.Println(soma, subtracao)
}

No exemplo acima, a função somarESubtrair recebe dois parâmetros do tipo int e retorna dois valores do tipo int. Ao chamarmos a função somarESubtrair podemos armazenar os valores retornados em variáveis.

A execução do código acima irá resultar no seguinte:

3 -1

Caso queiramos ignorar um dos valores retornados, podemos utilizar o caractere _ (underscore) para ignorar o valor.

package main

import "fmt"

func somarESubtrair(x int, y int) (int, int) {
  soma := x + y
  subtracao := x - y
  return soma, subtracao
}

func main() {
  soma, _ := somarESubtrair(1, 2)
  fmt.Println(soma)
}

A execução do código acima irá resultar no seguinte:

3

Funções variádicas

Google Cloud - Primeiros Passos
Curso Google Cloud - Primeiros Passos
Conhecer o curso

Uma função variádica é uma função que pode receber um número variável de parâmetros. Para definirmos uma função variádica, basta definirmos um parâmetro do tipo ...tipo, onde tipo é o tipo dos parâmetros que a função irá receber.

Vamos ver um exemplo de função variádica que recebe um número variável de parâmetros do tipo int e retorna a soma de todos os parâmetros.

package main

import "fmt"

func somar(x ...int) int {
  soma := 0
  for _, v := range x {
    soma += v
  }
  return soma
}

func main() {
  resultado := somar(1, 2, 3, 4, 5)
  fmt.Println(resultado)
}

Ao chamarmos a função somar podemos passar um número variável de parâmetros, desde que, todos sejam do tipo int.

A execução do código acima irá resultar no seguinte:

15

Quando utilizamos uma função variádica, o parâmetro variádico, na prática, se torna um slice, ou seja, podemos utilizar todos os recursos que temos para slices. Então o código acima poderia ser escrito da seguinte forma:

package main

import "fmt"

func somar(x []int) int {
  soma := 0
  for _, v := range x {
    soma += v
  }
  return soma
}

func main() {
  numeros := []int{1, 2, 3, 4, 5}
  resultado := somar(numeros)
  fmt.Println(resultado)
}

O código acima faz a mesma coisa que o código anterior, mas ao invés de passarmos um número variável de parâmetros, passamos um slice de inteiros.

É importante ressaltar que uma função variádica pode receber um número variável de parâmetros, mas apenas um parâmetro variádico por função. Além disso, o parâmetro variádico deve ser o último parâmetro da função, caso contrário, teremos um erro de compilação.

Funções recursivas

Uma função recursiva em Go é uma função que chama a si mesma. As funções recursivas podem ser muito úteis para resolver problemas que podem ser divididos em subproblemas menores e semelhantes, como calcular o fatorial de um número, gerar a sequência de Fibonacci ou percorrer uma estrutura de dados em árvore.

Aqui está um exemplo de uma função recursiva em Go que calcula o fatorial de um número:

package main

import "fmt"

func fatorial(x int) int {
  if x == 0 {
    return 1
  }
  return x * fatorial(x-1)
}

func main() {
  resultado := fatorial(5)
  fmt.Println(resultado)
}

Esta função calcula o fatorial de do parâmetro x verificando se x é igual a 0 e retornando 1 em caso verdadeiro. Caso contrário, a função retorna x multiplicado pelo resultado da chamada recursiva da função fatorial passando como parâmetro x-1. Essa chamada recursiva para a função fatorial é a parte mais importante da função, pois é ela que faz com que a função seja executada várias vezes até que o valor de x seja igual a 0.

Isso permite que a função calcule o fatorial de um número, pois o fatorial de um número é igual ao número multiplicado pelo fatorial do número anterior. Por exemplo, o fatorial de 5 é igual a 5 * 4 * 3 * 2 * 1, igual a 120.

Dessa maneira conseguimos utilizar uma função recursiva para dividir o problema de calcular um fatorial em subproblemas menores e semelhantes.

A execução do código acima irá resultar no seguinte:

120

Funções anônimas

Funções anônimas na Golang são funções que não estão vinculadas a um nome específico. Elas são úteis em várias situações, incluindo:

  • Como uma função de curta duração que só é necessária em um lugar. Isso pode tornar seu código mais limpo e fácil de ler, porque você não precisa dar um nome à função e pode defini-la exatamente onde ela é usada.
  • Como um parâmetro de função. Em Go, as funções são valores de primeira classe, o que significa que podem ser passadas para outras funções como argumentos. Isso permite que você escreva funções de ordem superior que usam outras funções como argumentos, o que pode ser muito poderoso.
  • Como um valor de retorno de função. Em Go, funções podem ser retornadas de outras funções como valores de retorno. Isso permite que você escreva funções que criam e retornam novas funções, o que pode ser útil para implementar recursos como memoização e avaliação preguiçosa.

Vamos ver um exemplo de função anônima que recebe dois parâmetros do tipo int e retorna um valor do tipo int.

package main

import "fmt"

func main() {
  soma := func(x int, y int) int {
    return x + y
  }(1, 2)
  fmt.Println(soma)
}

No exemplo acima, a função anônima é chamada imediatamente após a sua definição, passando os valores 1 e 2 como parâmetros. O resultado da função anônima é armazenado na variável soma.

A execução do código acima irá resultar no seguinte:

3

Funções de ordem superior

Funções de ordem superior são funções que recebem outras funções como parâmetros ou retornam funções como valores de retorno. As funções de ordem superior permitem que você escreva um código mais abstrato e flexível ao abstrair funções na Golang.

Aqui está um exemplo de uma função de ordem superior chamada mapper, que recebe um slice de inteiros e uma função como parâmetros e retorna um novo slice de inteiros. A função passada como parâmetro é aplicada a cada elemento do slice de inteiros e o resultado é armazenado no novo slice de inteiros.

package main

import "fmt"

func mapper(numeros []int, f func(int) int) []int {
  // Cria um novo slice de inteiros com o mesmo tamanho do slice de inteiros passado como parâmetro
  resultado := make([]int, len(numeros))

  // Aplica a função passada como parâmetro a cada elemento do slice de inteiros
  for i, v := range numeros {
    resultado[i] = f(v)
  }

  // Retorna o novo slice de inteiros
  return resultado
}

func main() {
  numeros := []int{1, 2, 3, 4, 5}
  quadrados := mapper(numeros, func(x int) int {
    return x * x
  })
  fmt.Println(quadrados)
}

Esta função mapper recebe um slice de inteiros e uma função como parâmetros. A função passada como parâmetro é aplicada a cada elemento do slice de inteiros e o resultado é armazenado no novo slice de inteiros. O novo slice de inteiros é retornado pela função mapper.

Nesse exemplo, estamos passando uma função anônima como parâmetro para a função mapper. Essa função anônima recebe um valor do tipo int e retorna o valor multiplicado por ele mesmo. Ou seja, essa função anônima calcula o quadrado de um número.

A execução do código acima irá resultar no seguinte:

[1 4 9 16 25]

Closure

Clean Code e SOLID - Escrevendo código limpo e escalável
Curso Clean Code e SOLID - Escrevendo código limpo e escalável
Conhecer o curso

Uma closure em Go é a combinação de uma função com as referências ao estado que a circunda (o ambiente léxico). Em outras palavras, uma closure é uma função que “fecha” o ambiente em que está definida, capturando as variáveis desse ambiente e permitindo que a função acesse e manipule essas variáveis mesmo após a alteração do ambiente.

Closures em Go são úteis porque permitem capturar o estado do ambiente local e usá-lo em uma função mesmo depois que o ambiente local foi alterado. Isso pode ser muito útil para escrever funções que precisam manter o estado ou que precisam ser chamadas várias vezes com diferentes argumentos, mas onde os argumentos estão relacionados de alguma forma.

Aqui está um exemplo de uma função que retorna uma closure:

package main

import "fmt"

func incrementador() func() int {
  // A variável x é definida no escopo da função incrementador
  x := 0

  // A função anônima retornada pela função incrementador faz referência à variável x
  return func() int {
    // A variável x é incrementada a cada vez que a closure é executada
    x++
    return x
  }
}

func main() {
  incrementa := incrementador()
  fmt.Println(incrementa())
  fmt.Println(incrementa())
  fmt.Println(incrementa())
}

Esta função incrementador retorna uma closure que incrementa um valor inteiro e retorna o valor incrementado. A variável x é definida no escopo da função incrementador e, é incrementada a cada vez que a closure é executada. A cada vez que a função incrementador é chamada, ela cria uma variável x e retorna uma nova closure que incrementa essa variável.

Ao executar o código acima teremos o seguinte resultado:

1
2
3

Conclusão

Neste artigo, aprendemos as sobre funções na Golang. Vimos o que são e quais as partes que a compõem. Também vimos como declarar funções, como passar parâmetros e como retornar valores.

Também vimos como declarar funções anônimas e como retornar funções de outras funções. Por fim, vimos como criar closures em Go.

Com esse conhecimento, você já pode começar a escrever funções na Golang e criar programas mais complexos, reutilizando código e escrevendo código mais limpo e legível.

Espero que tenha gostado do artigo. Se você tiver alguma dúvida ou sugestão, deixe um comentário abaixo.

Por fim, caso queira aprender mais sobre a Go e sua infinidade de recursos saiba que aqui na TreinaWeb temos o curso Go Básico que possui 05h30 de vídeo e um total de 37 exercícios.

Veja quais são os tópicos abordados durante o curso Go Básico:

  • Compreender a sintaxe básica da Golang;
  • Compreender conceitos básicos envolvidos na Go, como ponteiros de memória;
  • Utilizar as estruturas básicas da linguagem, como declaração de variáveis;
  • Utilizar as principais estruturas de conjuntos da Go, como arrays, slices e maps;
  • Entender as principais funções na Golang que são built-in, como make(), new(), panic(), recover() e defer;
  • Organizar o código em pacotes e utilizar os principais pacotes disponibilizados pela linguagem;
  • Entender como podemos utilizar concorrência com a Golang, inclusive com técnicas como os channels;
  • Entender o que são as structs na Go e como podemos utilizar um pouco de orientação a objetos com a linguagem;
  • Realizar operações de I/O no sistema operacional, como a criação e escrita de arquivos.

Autor(a) do artigo

Cleyson Lima
Cleyson Lima

Professor, programador, fã de One Piece e finge saber cozinhar. Cleyson é graduando em Licenciatura em Informática pelo IFPI - Campus Teresina Zona Sul, nos anos de 2019 e 2020 esteve envolvido em vários projetos coordenados pela secretaria municipal de educação da cidade de Teresina, onde o foco era introduzir alunos da rede pública no mundo da programação e robótica. Hoje é instrutor dos cursos de Spring na TreinaWeb, mas diz que seu coração sempre pertencerá ao Python.

Todos os artigos

Artigos relacionados Ver todos