Conteúdo

Funções

O que são?

Imagine que você queira executar uma atividade, como, por exemplo, fazer um miojo (grande salvador dos dias da preguiça). Um passo a passo simples para isso seria: pegar o alimento, cozinhar e servir, certo? Nesse fluxo, não há preocupação em como cada passo será feito, mas sabe-se que para conseguir o resultado desejado essas etapas devem ser feitas.

Essa abstração do que está sendo feito em detalhes é exatamente o que uma função faz. Ou seja, dada uma certa entrada (podendo ser nenhuma), executa-se ações que levam a um resultado desejado, podendo ou não ter saídas.

Utilizar funções facilita muito o dia a dia de quem desenvolve, pois com elas conseguimos:

  • Quebrar problemas grandes em problemas menores

  • Tornar o código mais fácil de entender

  • Reutilizar código

Por isso, use e abuse sempre que possível, e fizer sentido, de funções \o/

Exemplo: função soma

package main

import (
	"fmt"
)

func main() {
	s := Soma(2,3)
	fmt.Println(s)
}

// Soma recebe dois números inteiros positivos (entradas), realiza sua
//soma (ações) e retorna o resultado (saída).
func Soma(a int, b int) int {
	return a + b
}

Sintaxe:

No exemplo acima, tivemos contato com a sintaxe de Go para declaração de funções, onde:

  • func representa a palavra chave para iniciar declaração de função

  • Soma é o nome da função, obedece a máxima de quando iniciar com maiúscula é visível fora do package, e com minúscula apenas dentro

  • (a int, b int) são os parâmetros, as entradas da função, que seguem a sintaxe <nome> <tipo>, sempre separados por vírgula dentro do parênteses

  • int representa o tipo do retorno

  • {}, as chaves, são utilizadas para definir o escopo da função, onde ela começa e termina

  • return, é a palavra chave que utilizamos para retornar as saídas quando existe um retorno declarado

Tendo em mente essa sintaxe, aprofundaremos a seguir nos argumentos, e em seguida no retorno

Parâmetros de uma função em Go

Conforme dito anteriormente, os parâmetros da função são declarados logo após do nome na sintaxe para declaração de funções, e cada parâmetro por padrão segue a convenção <nome> <tipo>, sempre separados por vírgula dentro dos parênteses.

O que ainda não foi dito é que Go nos dá um açúcar sintático para facilitar nossa vida quando temos variáveis de um mesmo tipo. Dessa forma, o cabeçalho do exemplo anterior mudaria:

  • De: func Soma(a int, b int) int { }

  • Para: func Soma(a, b int) int { }

Além disso, como um recurso poderosíssimo da linguagem, temos ponteiros entre os tipos, o que nos proporciona a possibilidade de trabalhar com o próprio valor recebido como argumento da função, e não uma cópia.

Ponteiros

Mencionamos ponteiros no parágrafo anterior. Esse é um assunto para outro momento mas, caso queira saber mais já, você pode ler nesse link aqui.

Exemplo: funções SomaDoisPonteiro e SomaDois

package main

import (
	"fmt"
)

func main() {

	a := 2
	b := 2
	c := 2

	SomaDoisPont(&a) // Como passamos a refêrencia (ponteiro) ao número, funcionará
	SomaDois(b)      // Como não passamos a referência e não pegamos o retorno, não funcionará
	c = SomaDois(c)  // Como pegamos o retorno, funcionará

	fmt.Printf("a:%d, b:%d, c:%d", a, b, c)
}

// SomaDoisPonteiro recebe uma referência (ponteiro) para um número qualquer
// e adiciona dois a ele.
func SomaDoisPonteiro(n *int) {
	*n = Soma(*n, 2)
}

// SomaDois recebe um número qualquer, adiciona dois a ele e retorna o resultado.
func SomaDois(n int) int {
	n = Soma(n, 2)
	return n
}

// Soma recebe dois números inteiros positivos (entradas), realiza sua soma (ações)
//e retorna o resultado (saída).
func Soma(a, b int) int {
	return a + b
}

Retornos de uma função em Go

Talvez você não tenha reparado, mas nossa função Soma() não obedece à regra de negócio estipulada a ela. Afinal, podemos passar números negativos como argumentos, e não recebemos nenhum erro sobre isso.

Dessa forma, passarems a fazer essa validação, e nossa função ficará assim:

package main

import (
	"fmt"
)

func main() {
	s, err := Soma(3, 1)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(s)
	}

	s, err = Soma(-3, 1)
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(s)
	}
}

// Soma recebe dois números inteiros positivos (entradas), realiza sua soma (ações)
// e retorna o resultado e um erro (saída).
func Soma(a, b int) (int, error) {
	if a <= 0 || b <= 0 {
		return 0, fmt.Errorf("todas as entradas precisam ser positivas, favor avaliar e tentar novamente")
	}
	return a + b, nil
}

error é uma interface, para saber mais visite esse link aqui (em inglês)

Como é possível perceber no código acima, em Go temos a possibilIdade de ter mais de uma saída de uma função, obedecendo uma sintaxe parecida com a de entradas.

Inclusive, esse é um “padrão” muito importante e bem comum de se ver no mundo Go para tratamento de erros.

Bônus: funções variádicas

Nossa função Soma() já tem um resultado muito bom e conciso, mas pra ficar melhor ainda queremos poder somar qualquer quantidade de números, não apenas dois como viemos fazendo. E isso é possível com um recurso poderosíssimo, as chamadas funções variádicas, presentes em Go e em outras linguagens.

Com esse recurso, conseguimos receber um número qualquer de argumentos de um dado tipo, na forma de um iterável. Vejamos o exemplo a seguir com nossa função Soma() refatorada, em um caso de erro e um caso de sucesso.

package main

import (
	"fmt"
)

func main() {
	soma1, err1 := Soma(1, -2, 3, 4)
	if err1 != nil {
		fmt.Println("erro: ", err1)
	} else {
		fmt.Println("soma: ", soma1)
	}

	soma2, err2 := Soma(1, 2, 3, 4)
	if err2 != nil {
		fmt.Println("erro: ", err2)
	} else {
		fmt.Println("soma: ", soma2)
	}
}

// Soma recebe n números inteiros positivos (entradas), realiza sua soma (ações)
// e retorna  o resultado (saída).
func Soma(numeros ...int) (int, error) {
	var soma int
	for _, numero := range numeros {
		if numero <= 0 {
			return 0, fmt.Errorf("número %v não é positivo, e não é aceito nessa operação",
				numero)
		}
		soma += numero
	}
	return soma, nil
}

Outra forma de utilizar esse recurso é adicionando ... após um conjunto de elementos, como no argumento passado para a função Soma() no exemplo abaixo:

package main

import (
	"fmt"
)

func main() {
	numeros := []int{0, 2, 3, 4}
	soma, err := Soma(numeros...)
	if err != nil {
		fmt.Println("erro: ", err)
		return
	}
	fmt.Println("soma: ", soma)
}

// Soma recebe n números inteiros positivos (entradas), realiza sua soma (ações)
// e retorna  o resultado (saída).
func Soma(numeros ...int) (int, error) {
	var soma int
	for _, numero := range numeros {
		if numero <= 0 {
			return 0, fmt.Errorf("número %v não é positivo, e não é aceito nessa operação",
				numero)
		}
		soma += numero
	}
	return soma, nil
}

Last updated