Criar funções
Em Go, as funções permitem agrupar um conjunto de instruções que você pode chamar de outras partes do seu aplicativo. Em vez de criar um programa com muitas instruções, você pode usar funções para organizar o código e torná-lo mais legível. Um código mais legível também é mais fácil de manter.
Até este ponto, temos chamado a fmt.Println()
função, e temos escrito código na main()
função. Nesta seção, exploraremos como você pode criar funções personalizadas. Também veremos algumas outras técnicas que você pode usar com funções em Go.
Função principal
A função com a qual você tem interagido é a main()
função. Todos os programas executáveis em Go têm esta função porque é o ponto de partida do programa. Você só pode ter uma main()
função no seu programa. Se você estiver criando um pacote Go, não precisará escrever uma main()
função. Veremos como criar pacotes em um próximo módulo.
Antes de entrarmos no básico da criação de funções personalizadas no Go, vamos examinar um aspeto crucial da main()
função. Como você deve ter notado, a main()
função não tem parâmetros e não retorna nada. Mas isso não significa que ele não possa ler valores do usuário, como argumentos de linha de comando. Se você precisar acessar argumentos de linha de comando em Go, você pode fazê-lo com o pacote os e a os.Args
variável, que contém todos os argumentos passados para o programa.
O código a seguir lê dois números da linha de comando e os resume:
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
number1, _ := strconv.Atoi(os.Args[1])
number2, _ := strconv.Atoi(os.Args[2])
fmt.Println("Sum:", number1+number2)
}
A os.Args
variável contém todos os argumentos de linha de comando passados para o programa. Como esses valores são do tipo string
, você precisa convertê-los para int
resumi-los.
Para executar o programa, use este comando:
go run main.go 3 5
Eis o resultado:
Sum: 8
Vamos ver como podemos refatorar o código acima e criar nossa primeira função personalizada.
Funções personalizadas
Aqui está a sintaxe para criar uma função:
func name(parameters) (results) {
body-content
}
Observe que você usa a func
palavra-chave para definir uma função e, em seguida, atribuir um nome a ela. Após o nome, especifique a lista de parâmetros para a função. Pode haver zero ou mais parâmetros. Você também pode definir os tipos de retorno da função, dos quais também pode haver zero ou mais. (Falaremos sobre o retorno de vários valores na próxima seção.) E depois de definir todos esses valores, você escreve o conteúdo do corpo da função.
Para praticar essa técnica, vamos refatorar o código da seção anterior para somar os números em uma função personalizada. Usaremos este código:
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
sum := sum(os.Args[1], os.Args[2])
fmt.Println("Sum:", sum)
}
func sum(number1 string, number2 string) int {
int1, _ := strconv.Atoi(number1)
int2, _ := strconv.Atoi(number2)
return int1 + int2
}
Esse código cria uma função chamada sum
que usa dois string
argumentos, os converte em e, em int
seguida, retorna o resultado de resumi-los. Quando você define um tipo de retorno, sua função precisa retornar um valor desse tipo.
Em Go, você também pode definir um nome para o valor de retorno de uma função, como se fosse uma variável. Por exemplo, você pode refatorar a sum
função da seguinte forma:
func sum(number1 string, number2 string) (result int) {
int1, _ := strconv.Atoi(number1)
int2, _ := strconv.Atoi(number2)
result = int1 + int2
return
}
Observe que agora você precisa colocar o valor do resultado da função entre parênteses. Você também pode usar a variável dentro da função e apenas adicionar uma return
linha no final. Go retornará os valores atuais dessas variáveis de retorno. A simplicidade de escrever a return
palavra-chave no final da função é atraente (especialmente quando há mais de um valor de retorno). Não recomendamos esta abordagem. Pode não estar claro o que a função está retornando.
Retornar vários valores
Em Go, uma função pode retornar mais de um valor. Você pode definir esses valores de forma semelhante à forma como define os parâmetros da função. Em outras palavras, você especifica um tipo e um nome, mas o nome é opcional.
Por exemplo, digamos que você queira criar uma função que soma dois números, mas também os multiplica. O código da função teria esta aparência:
func calc(number1 string, number2 string) (sum int, mul int) {
int1, _ := strconv.Atoi(number1)
int2, _ := strconv.Atoi(number2)
sum = int1 + int2
mul = int1 * int2
return
}
Agora você precisa de duas variáveis para armazenar os resultados da função. (Não será compilado de outra forma.) Veja como é:
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
sum, mul := calc(os.Args[1], os.Args[2])
fmt.Println("Sum:", sum)
fmt.Println("Mul:", mul)
}
Outro recurso interessante no Go é que, se você não precisar de um dos valores de retorno de uma função, poderá descartá-lo atribuindo o valor de retorno à _
variável. A _
variável é a maneira idiomática de Go ignorar valores de retorno. Ele permite que o programa para compilar. Então, se você quiser apenas o valor da soma, você pode usar este código:
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
sum, _ := calc(os.Args[1], os.Args[2])
fmt.Println("Sum:", sum)
}
Veremos mais sobre como ignorar valores de retorno de funções quando explorarmos o tratamento de erros em um módulo futuro.
Alterar valores de parâmetros de função (ponteiros)
Quando você passa um valor para uma função, cada alteração nessa função não afetará o chamador. Go é uma linguagem de programação "pass by value". Sempre que você passa um valor para uma função, Go pega esse valor e cria uma cópia local (uma nova variável na memória). As alterações feitas nessa variável na função não afetam a que você enviou para a função.
Por exemplo, digamos que você crie uma função para atualizar o nome de uma pessoa. Observe o que acontece quando você executa este código:
package main
import "fmt"
func main() {
firstName := "John"
updateName(firstName)
fmt.Println(firstName)
}
func updateName(name string) {
name = "David"
}
Mesmo que você tenha alterado o nome para "David" na função, a saída ainda é "John". A saída não foi alterada porque a updateName
alteração na função modifica apenas a cópia local. Go passou o valor da variável, não a variável em si.
Se você quiser que a updateName
alteração feita na função afete a firstName
variável na main
função, você precisa usar um ponteiro. Um ponteiro é uma variável que contém o endereço de memória de outra variável. Quando você envia um ponteiro para uma função, você não está passando um valor, você está passando um endereço de memória. Assim, cada alteração que você faz nessa variável afeta o chamador.
No Go, há dois operadores para trabalhar com ponteiros:
- O
&
operador toma o endereço do objeto que o segue. - O
*
operador desreferencia um ponteiro. Ele lhe dá acesso ao objeto no endereço contido no ponteiro.
Vamos modificar nosso exemplo anterior para esclarecer como os ponteiros funcionam:
package main
import "fmt"
func main() {
firstName := "John"
updateName(&firstName)
fmt.Println(firstName)
}
func updateName(name *string) {
*name = "David"
}
Execute o código anterior. Observe que a saída agora é exibida David
em vez de John
.
A primeira coisa que você precisa fazer é modificar a assinatura da função para indicar que deseja receber um ponteiro. Para fazer isso, altere o tipo de parâmetro de string
para *string
. (Este último ainda é uma string, mas agora é um ponteiro para uma string.) Em seguida, quando você atribui um novo valor a essa variável, você precisa adicionar a estrela (*
) no lado esquerdo da variável para produzir o valor dessa variável. Quando você chama a updateName
função, você não envia o valor, mas o endereço de memória da variável. O &
símbolo no lado esquerdo da variável indica o endereço da variável.