Créer des fonctions
En Go, les fonctions vous permettent de regrouper un ensemble d’instructions que vous pouvez appeler à partir d’autres parties de votre application. Au lieu de créer un programme avec de nombreuses instructions, vous pouvez utiliser des fonctions pour organiser le code et le rendre plus lisible. Un code plus lisible est également plus facile à gérer.
Jusqu’à présent, nous avons appelé la fonction fmt.Println()
et avons écrit du code dans la fonction main()
. Dans cette section, nous allons découvrir comment vous pouvez créer des fonctions personnalisées. Nous allons également examiner d’autres techniques que vous pouvez utiliser avec les fonctions en Go.
Fonction main
La fonction avec laquelle vous avez interagi est la fonction main()
. Tous les programmes exécutables en Go ont cette fonction, car il s’agit du point de départ du programme. Vous ne pouvez avoir qu’une seule fonction main()
dans votre programme. Si vous créez un package Go, vous n’avez pas besoin d’écrire de fonction main()
. Nous verrons comment créer des packages dans un prochain module.
Avant de passer aux principes de base de la création de fonctions personnalisées en Go, observons un aspect essentiel de la fonction main()
. Comme vous l’avez peut-être remarqué, la fonction main()
n’a aucun paramètre et ne retourne rien. Mais cela ne signifie pas qu’elle ne peut pas lire les valeurs de l’utilisateur, comme les arguments de ligne de commande. Si vous avez besoin d’accéder à des arguments de ligne de commande en Go, vous pouvez le faire avec le package os et la variable os.Args
, qui contient tous les arguments passés au programme.
Le code suivant lit deux nombres à partir de la ligne de commande et les additionne :
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)
}
La variable os.Args
contient tous les arguments de ligne de commande passés au programme. Ces valeurs étant de type string
, vous devez les convertir en int
pour les additionner.
Pour exécuter le programme, utilisez la commande suivante :
go run main.go 3 5
Voici le format :
Sum: 8
Voyons comment nous pouvons refactoriser le code ci-dessus et créer notre première fonction personnalisée.
Fonctions personnalisées
Voici la syntaxe pour créer une fonction :
func name(parameters) (results) {
body-content
}
Notez que vous utilisez le mot clé func
pour définir une fonction et lui affecter un nom. Après le nom, vous spécifiez la liste des paramètres de la fonction. Il peut y avoir zéro ou plusieurs paramètres. Vous pouvez aussi définir les types de retour de la fonction, dont le nombre peut également être égal à zéro ou plus. (Nous aborderons le retour de plusieurs valeurs dans la section suivante.) Une fois que vous avez défini toutes ces valeurs, vous écrivez le contenu du corps de la fonction.
Pour appliquer cette technique, nous allons refactoriser le code de la section précédente afin d’additionner les nombres dans une fonction personnalisée. Nous allons utiliser ce code :
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
}
Ce code crée une fonction appelée sum
qui prend deux arguments string
, les caste en int
, puis retourne le résultat de leur addition. Quand vous définissez un type de retour, votre fonction doit retourner une valeur de ce type.
En Go, vous pouvez également définir un nom pour la valeur de retour d’une fonction, comme s’il s’agissait d’une variable. Par exemple, vous pouvez refactoriser la fonction sum
comme suit :
func sum(number1 string, number2 string) (result int) {
int1, _ := strconv.Atoi(number1)
int2, _ := strconv.Atoi(number2)
result = int1 + int2
return
}
Notez que vous devez maintenant placer la valeur de résultat de la fonction entre parenthèses. Vous pouvez également utiliser la variable à l’intérieur de la fonction et simplement ajouter une ligne return
à la fin. Go retourne les valeurs actuelles de ces variables de retour. La simplicité de l’écriture du mot clé return
à la fin de la fonction est attrayante (surtout quand vous avez plusieurs valeurs de retour). Nous ne recommandons pas cette approche. Il peut être difficile de savoir ce que la fonction retourne.
Retourner plusieurs valeurs
En Go, une fonction peut retourner plusieurs valeurs. Vous pouvez définir ces valeurs de manière similaire à la façon dont vous définissez les paramètres de la fonction. En d’autres termes, vous spécifiez un type et un nom, mais le nom est facultatif.
Par exemple, imaginons que vous souhaitiez créer une fonction qui additionne deux nombres, mais les multiplie également. Le code de la fonction ressemblerait à ceci :
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
}
Vous avez maintenant besoin de deux variables pour stocker les résultats de la fonction. (Sinon, la compilation est impossible.) Voici à quoi il ressemble :
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)
}
Une autre fonctionnalité intéressante de Go est que si vous n’avez pas besoin de l’une des valeurs de retour d’une fonction, vous pouvez l’ignorer en affectant la valeur de retour à la variable _
. La variable _
est la méthode idiomatique avec laquelle Go ignore les valeurs de retour. Elle permet au programme d’être compilé. Ainsi, si vous souhaitez uniquement la valeur de la somme, vous pouvez utiliser ce code :
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
sum, _ := calc(os.Args[1], os.Args[2])
fmt.Println("Sum:", sum)
}
Nous nous pencherons davantage sur la façon d’ignorer les valeurs de retour des fonctions quand nous explorerons la gestion des erreurs dans un prochain module.
Changer les valeurs des paramètres de fonction (pointeurs)
Quand vous passez une valeur à une fonction, chaque modification de cette fonction n’affecte pas l’appelant. Go est un langage de programmation de « passage par valeur ». Chaque fois que vous passez une valeur à une fonction, Go prend cette valeur et crée une copie locale (une nouvelle variable en mémoire). Les modifications que vous apportez à cette variable dans la fonction n’affectent pas celle que vous avez envoyée à la fonction.
Par exemple, imaginons que vous créez une fonction pour mettre à jour le nom d’une personne. Notez ce qui se passe quand vous exécutez ce code :
package main
import "fmt"
func main() {
firstName := "John"
updateName(firstName)
fmt.Println(firstName)
}
func updateName(name string) {
name = "David"
}
Même si vous avez changé le nom en « David » dans la fonction, la sortie est toujours « John ». La sortie n’a pas changé, car la modification de la fonction updateName
modifie uniquement la copie locale. Go a passé la valeur de la variable, et non la variable elle-même.
Si vous souhaitez que la modification que vous apportez à la fonction updateName
affecte la variable firstName
dans la fonction main
, vous devez utiliser un pointeur. Un pointeur est une variable qui contient l’adresse mémoire d’une autre variable. Quand vous envoyez un pointeur à une fonction, vous ne transférez pas de valeur. Vous transférez une adresse mémoire. Ainsi, chaque modification que vous apportez à cette variable affecte l’appelant.
En Go, vous pouvez utiliser deux opérateurs avec les pointeurs :
- L’opérateur
&
prend l’adresse de l’objet qui le suit. - L’opérateur
*
déréférence un pointeur. Il vous donne accès à l’objet à l’adresse contenue dans le pointeur.
Nous allons modifier notre exemple précédent pour clarifier le fonctionnement des pointeurs :
package main
import "fmt"
func main() {
firstName := "John"
updateName(&firstName)
fmt.Println(firstName)
}
func updateName(name *string) {
*name = "David"
}
Exécutez le code précédent. Notez que la sortie affiche maintenant David
au lieu de John
.
La première chose à faire est de modifier la signature de la fonction pour indiquer que vous souhaitez recevoir un pointeur. Pour ce faire, remplacez le type de paramètre string
par *string
. (Ce dernier est toujours une chaîne, mais il s’agit maintenant d’un pointeur vers une chaîne.) Ensuite, quand vous affectez une nouvelle valeur à cette variable, vous devez ajouter l’étoile (*
) à gauche de la variable pour obtenir la valeur de cette variable. Quand vous appelez la fonction updateName
, vous n’envoyez pas la valeur, mais l’adresse mémoire de la variable. Le symbole &
à gauche de la variable indique l’adresse de la variable.