1 Conceitos Básicos de Funções

Na programação, uma função é um trecho de código que realiza uma tarefa específica e pode ter parâmetros de entrada e valores de retorno. Na linguagem Go, as funções são amplamente utilizadas para organizar e reutilizar código. Utilizar funções de forma eficaz pode tornar o código mais conciso, altamente legível e fácil de manter.

As funções são um componente essencial da linguagem Go, e entender como declarar e definir funções é crucial para escrever código eficiente e legível.

2 Definindo Funções

2.1 Declaração de Função

Na linguagem Go, a forma geral de declaração de função é:

func nomeFuncao(parâmetros) tipoRetorno {
    // Corpo da função
}

Vamos analisar esses componentes:

  1. A palavra-chave func é usada para declarar uma função.
  2. nomeFuncao é o nome da função, seguindo as convenções de nomenclatura da linguagem Go. Uma função com a primeira letra maiúscula é exportável, o que significa que é visível fora do pacote; uma função com a primeira letra minúscula não é exportável e só pode ser usada dentro do mesmo pacote.
  3. parâmetros é a lista de parâmetros que a função recebe, separados por vírgulas. O tipo de cada parâmetro precisa ser especificado, e se múltiplos parâmetros têm o mesmo tipo, o tipo só precisa ser especificado uma vez.
  4. tipoRetorno é o tipo do valor de retorno da função. Se a função não retornar um valor, esta parte pode ser omitida. Se a função retornar múltiplos valores, eles precisam ser colocados entre parênteses.

Por exemplo, podemos declarar uma função simples para calcular a soma de dois inteiros:

func Somar(a int, b int) int {
    return a + b
}

Neste exemplo, o nome da função é Somar, ela recebe dois parâmetros do tipo int (a e b), e retorna a soma deles com o tipo de retorno int.

2.2 Lista de Parâmetros

A lista de parâmetros define quais parâmetros a função aceita e o tipo de cada parâmetro. Parâmetros são um tipo especial de variável usada para passar dados para uma função.

func Saudacao(nome string, idade int) {
    fmt.Printf("Olá, %s! Você tem %d anos.\n", nome, idade)
}

Nesta função Saudacao, nome e idade são parâmetros. O tipo de nome é string, e o tipo de idade é int. Ao chamar esta função, valores reais devem ser fornecidos para esses parâmetros.

2.3 Tipos de Retorno

Funções podem retornar resultados computados, e o tipo de retorno define o tipo de dados do valor retornado pela função. Funções podem não ter valor de retorno, ou podem ter um ou mais valores de retorno.

Se uma função tiver múltiplos valores de retorno, seus tipos devem ser colocados entre parênteses durante a declaração:

func Dividir(dividendo, divisor float64) (float64, error) {
    if divisor == 0 {
        return 0, errors.New("não é possível dividir por zero")
    }
    return dividendo / divisor, nil
}

A função Dividir aqui retorna dois valores: o quociente e uma mensagem de erro. Se o divisor for zero, um erro é retornado.

2.4 Parâmetros Variádicos

Em Go, quando não temos certeza de quantos parâmetros o chamador irá passar ao definir uma função, podemos usar parâmetros variádicos. Parâmetros variádicos são indicados por reticências ..., significando que a função pode aceitar qualquer número de parâmetros. Isso é muito útil ao lidar com uma quantidade incerta de dados ou ao implementar certos tipos de funcionalidades como formatação, síntese ou iteração.

Cenários de Aplicação

Os parâmetros variádicos são comumente usados nos seguintes cenários:

  1. Concatenação de strings: como as funções fmt.Sprintf e strings.Join.
  2. Processamento de array/fatia: ao lidar com arrays ou fatias de comprimentos variáveis, como calcular a soma ou concatenar múltiplas fatias.
  3. Registro e tratamento de erros: ao lidar com múltiplos erros ou registrando múltiplas informações de log, como log.Printf ou funções customizadas de resumo de erros.
  4. Funções auxiliares: utilizadas em APIs ou bibliotecas para fornecer aos usuários maneiras mais flexíveis de chamar as funções.

Utilizando Parâmetros Variáveis

Aqui está um exemplo simples de utilização de parâmetros variáveis:

// Define uma função que recebe um número variável de parâmetros inteiros e retorna a soma deles
func Soma(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}

func main() {
    // Você pode passar qualquer número de parâmetros
    fmt.Println(Soma(1, 2))          // Saída: 3
    fmt.Println(Soma(1, 2, 3, 4))    // Saída: 10
    
    // Também é possível passar um slice e utilizar o reticências após o slice
    numeros := []int{1, 2, 3, 4, 5}
    fmt.Println(Soma(numeros...))    // Saída: 15
}

Na função Soma, nums é um slice de inteiros que contém todos os parâmetros passados para a função Soma. Podemos usar um loop de intervalo (range loop) para iterar por este slice e calcular a soma.

Vale ressaltar que os parâmetros variáveis dentro da função são na verdade um slice, então todas as operações relacionadas a slices podem ser utilizadas. Ao precisar passar um slice como um parâmetro variável para uma função, basta adicionar reticências ... após o slice.

A utilização de parâmetros variáveis pode tornar as chamadas de função mais flexíveis e concisas, mas também terá um leve impacto na performance, pois a utilização de parâmetros variáveis envolve a criação de slices e alocação de memória. Portanto, cautela deve ser exercida quando requisitos rigorosos de desempenho estiverem em vigor.

Dica: Uma explicação detalhada sobre slices será fornecida em capítulos posteriores. Se você tem experiência com outras linguagens de programação, pode temporariamente pensar neles como arrays.

3 Chamando Funções

3.1 Chamadas Básicas de Funções

Chamar uma função significa executar o código da função. Em Go, chamar uma função definida é muito simples, basta usar o nome da função e passar os parâmetros apropriados. Por exemplo:

resultado := adicionar(3, 4)
fmt.Println(resultado)  // Saída: 7

Neste exemplo, a função adicionar é chamada e dois inteiros são passados como parâmetros e, em seguida, o resultado retornado é atribuído à variável resultado.

3.2 Passagem de Parâmetros

Ao passar parâmetros para uma função, Go padrão utiliza a passagem por valor, o que significa passar uma cópia do valor do parâmetro, e os dados originais não serão alterados. No entanto, se você deseja que a função modifique variáveis externas ou por razões de desempenho, você pode usar passagem por referência, como usar ponteiros ou passar structs grandes.

Aqui estão exemplos de passagem por valor e passagem por referência:

// Exemplo de passagem por valor
func dobrar(val int) {
    val *= 2
}

// Exemplo de passagem por referência
func dobrarPtr(val *int) {
    *val *= 2
}

func main() {
    valor := 3
    dobrar(valor)
    fmt.Println(valor)  // Saída 3, o valor permanece inalterado

    dobrarPtr(&valor)
    fmt.Println(valor)  // Saída 6, valor dobrado
}

No exemplo acima, a função dobrar tenta dobrar o val passado, mas apenas dobra sua cópia, deixando a variável original valor inalterada. No entanto, a função dobrarPtr altera o valor da variável original ao receber um ponteiro para uma variável do tipo inteiro como parâmetro.