Ecrire des fonctions en GO
Mise à jour :
Dans un précédent guide sur la découverte du langage Go, j’ai présenté les bases de Golang : sa syntaxe épurée, sa rapidité de compilation et son approche minimaliste. Maintenant que vous avez une première vue d’ensemble, il est temps de passer à l’étape suivante: écrire des fonctions en Go. C’est un passage incontournable pour structurer votre code, le rendre réutilisable, testable et surtout, plus lisible. Dans ce guide, je vais vous montrer comment déclarer, utiliser et documenter des fonctions de manière simple et efficace.
Déclaration d’une fonction en Go
Écrire une fonction en Go, c’est simple, mais il faut bien comprendre la
structure pour l’utiliser efficacement. En Go, tout commence par le mot-clé
func
, suivi du nom de la fonction, des parenthèses pour les paramètres, et
enfin du type de retour (si nécessaire).
Voici la syntaxe de base :
func nomDeLaFonction(paramètres) typeRetour { // corps de la fonction}
Prenons un exemple minimaliste :
func direBonjour() { fmt.Println("Bonjour !")}
Cette fonction ne prend aucun paramètre et ne retourne aucune valeur. Elle se contente d’afficher un message à l’écran. Pour l’appeler, rien de plus simple :
direBonjour()
En Go, il est courant de grouper plusieurs fonctions dans un même fichier pour structurer ses actions, surtout dans les programmes orientés scripts ou outils systèmes.
💡 À noter: une fonction dont le nom commence par une majuscule (ex.
AfficherMessage
) est exportée. Cela signifie qu’elle peut être utilisée
depuis un autre package. Si le nom commence par une minuscule, elle reste
privée au package actuel.
Exemple de fonction exportée :
func Addition(a int, b int) int { return a + b}
Avec cette base, on peut commencer à créer des blocs de logique réutilisables dans n’importe quel projet Go. Passons maintenant à la manière de leur transmettre des paramètres pour qu’elles deviennent vraiment utiles.
Fonctions avec paramètres
Pour qu’une fonction soit réellement utile, il faut souvent lui passer des données à traiter. En Go, les paramètres sont définis entre parenthèses, juste après le nom de la fonction. Chaque paramètre doit être accompagné de son type.
Voici un exemple simple :
func saluer(nom string) { fmt.Println("Bonjour", nom)}
Ici, la fonction saluer
prend un paramètre nom
de type string
. Lors de
l’appel, on lui transmet une valeur :
saluer("Alice")
Et le programme affichera :
Bonjour Alice
Plusieurs paramètres
Vous pouvez évidemment passer plusieurs paramètres à une fonction :
func afficherInfos(nom string, age int) { fmt.Printf("%s a %d ans\n", nom, age)}
Appel de la fonction :
afficherInfos("Marc", 34)
Cette syntaxe est claire, mais on peut aussi factoriser les types quand plusieurs paramètres sont du même type :
func addition(a, b int) { fmt.Println("Résultat :", a + b)}
Passage par valeur
En Go, les paramètres sont passés par valeur par défaut. Cela signifie que la fonction travaille sur une copie des données. Si vous modifiez un paramètre dans la fonction, cela n’affecte pas la variable d’origine.
Exemple :
func doubler(val int) { val = val * 2}
func main() { x := 10 doubler(x) fmt.Println(x) // Affiche toujours 10}
Pour modifier une variable dans la fonction, il faut passer un pointeur (on y reviendra plus loin).
Utiliser les paramètres permet de rendre vos fonctions dynamiques et génériques, ce qui est essentiel pour écrire du code propre et réutilisable. Prêt à aller plus loin ? Voyons maintenant comment récupérer une valeur de retour depuis une fonction.
Fonctions avec valeurs de retour
En plus de recevoir des paramètres, une fonction en Go peut retourner une ou plusieurs valeurs. Cela permet de transmettre un résultat calculé à l’endroit où la fonction a été appelée.
La syntaxe est simple : après les parenthèses contenant les paramètres, on indique le type de retour.
Exemple de fonction avec un retour simple
func doubler(x int) int { return x * 2}
Ici, doubler
prend un int
en entrée et retourne un int. Pour utiliser le
résultat :
résultat := doubler(5)fmt.Println(résultat) // Affiche 10
Retourner plusieurs valeurs
C’est l’une des particularités de Go : une fonction peut retourner plusieurs valeurs. Cela s’avère très utile pour gérer les erreurs, par exemple.
func diviser(a, b int) (int, error) { if b == 0 { return 0, fmt.Errorf("division par zéro") } return a / b, nil}
Utilisation :
résultat, err := diviser(10, 2)if err != nil { fmt.Println("Erreur :", err)} else { fmt.Println("Résultat :", résultat)}
Nommage des valeurs de retour
On peut aussi nommer les valeurs de retour directement dans la déclaration de la fonction :
func calcul(a, b int) (somme int, produit int) { somme = a + b produit = a * b return // retour implicite}
Ce style est moins courant, mais utile dans certains cas pour améliorer la lisibilité.
Les valeurs de retour sont une brique essentielle en Go. Elles permettent de chaîner des opérations, structurer les erreurs, et clarifier le flux des données. Dans le chapitre suivant, on va voir comment aller encore plus loin avec des fonctions acceptant un nombre variable d’arguments.
Fonctions variadiques
En Go, une fonction variadique peut accepter un nombre variable d’arguments d’un même type. C’est très pratique quand on ne connaît pas à l’avance le nombre exact de valeurs à traiter.
Syntaxe d’une fonction variadique
On utilise ...
avant le type du dernier paramètre :
func afficherNombres(nombres ...int) { for _, n := range nombres { fmt.Println(n) }}
Appel possible avec autant d’arguments que souhaité :
afficherNombres(1, 2, 3)afficherNombres(10, 20)
Le paramètre nombres
est traité comme un slice ([]int
). On peut donc
aussi lui passer directement une slice :
liste := []int{4, 5, 6}afficherNombres(liste...) // attention au "..."
Exemple plus avancé : calculer la somme
func somme(nombres ...int) int { total := 0 for _, n := range nombres { total += n } return total}
Appel :
fmt.Println(somme(1, 2, 3)) // Affiche 6
À savoir
- Une fonction peut avoir d’autres paramètres avant le variadique, mais seulement un seul paramètre variadique, et il doit être le dernier.
- Les fonctions variadiques sont souvent utilisées dans les fonctions
d’impression (
fmt.Println
,fmt.Printf
, etc.).
Les fonctions variadiques rendent votre code flexible et plus lisible, notamment lorsqu’il s’agit de gérer des listes d’éléments de taille variable. Maintenant, voyons une autre puissance de Go : les fonctions anonymes et les closures.
Fonctions anonymes et closures
En Go, il est possible de définir des fonctions anonymes : ce sont des fonctions sans nom, que l’on peut assigner à une variable ou exécuter immédiatement. Elles sont très utiles pour des traitements rapides, des callbacks ou encore des closures, c’est-à-dire des fonctions qui capturent l’environnement dans lequel elles sont définies.
Fonction anonyme assignée à une variable
f := func(nom string) { fmt.Println("Salut", nom)}f("Julien") // Affiche : Salut Julien
Cette fonction est définie à la volée et peut être utilisée comme n’importe quelle fonction classique.
Exécution immédiate (IIFE)
Il est aussi possible d’exécuter une fonction anonyme immédiatement après sa définition. Cela s’appelle une Immediately Invoked Function Expression (IIFE), très utile pour encapsuler du code temporaire :
func(x int) { fmt.Println("Valeur :", x)}(42)
Closure : fonction qui capture des variables
Une closure permet à une fonction d’accéder à des variables extérieures même après que la fonction englobante a terminé son exécution. Exemple classique : un compteur.
func compteur() func() int { i := 0 return func() int { i++ return i }}
Utilisation :
compte := compteur()fmt.Println(compte()) // 1fmt.Println(compte()) // 2fmt.Println(compte()) // 3
Chaque appel à compteur()
crée une nouvelle instance avec sa propre variable
i
. Ce comportement est très puissant pour créer des fonctions
personnalisées, ou simuler de l’état localement sans avoir besoin de structures
complexes.
Les fonctions anonymes et closures sont des outils élégants pour simplifier le code, encapsuler du comportement, et manipuler des données de manière fonctionnelle. Prochaine étape : apprendre à bien documenter ses fonctions, pour soi et pour les autres.
Documentation des fonctions
En Go, documenter ses fonctions est essentiel pour écrire du code clair, maintenable et compréhensible, surtout lorsqu’on travaille en équipe ou qu’on crée une bibliothèque partagée.
Comment écrire un commentaire de fonction
La règle est simple : un commentaire de fonction commence par le nom de la fonction, suivi d’une description courte et précise. Ce commentaire doit être placé juste au-dessus de la fonction.
// Saluer affiche un message de salutation personnalisé.func Saluer(nom string) { fmt.Println("Bonjour", nom)}
Cela permet aux outils comme godoc
ou pkg.go.dev ↗
d’afficher automatiquement la documentation générée.
Quelques conseils pour bien commenter :
- Toujours commencer le commentaire par le nom de la fonction.
- Utiliser des phrases simples et actives.
- Décrire le rôle de la fonction et, si besoin, les paramètres et les valeurs de retour.
- Ne pas expliquer ce que le code fait ligne par ligne, mais ce qu’il accomplit globalement.
Exemple plus complet :
// Diviser retourne le résultat de a divisé par b.// Si b vaut zéro, une erreur est retournée.func Diviser(a, b int) (int, error) { if b == 0 { return 0, fmt.Errorf("division par zéro") } return a / b, nil}
Ce type de commentaire rend la fonction auto-documentée, ce qui est particulièrement utile lorsqu’on navigue dans un gros projet ou une base de code ouverte.
Résumé
En résumé, une bonne documentation commence par des fonctions bien nommées, mais elle repose aussi sur des commentaires clairs et précis. Un effort minime pour un gain énorme sur le long terme.
Prêt à améliorer encore plus votre code ? Je vous montre maintenant les bonnes pratiques pour écrire des fonctions efficaces.
Bonnes pratiques
Écrire des fonctions en Go, ce n’est pas seulement une question de syntaxe. Il y a tout un état d’esprit à adopter pour rendre son code plus lisible, modulaire et facile à maintenir. Voici les bonnes pratiques que j’applique systématiquement lorsque je développe en Go.
1. Une fonction = une responsabilité
Chaque fonction doit faire une seule chose. Si une fonction commence à gérer plusieurs tâches, il est temps de la scinder.
Mauvais exemple :
func traiterUtilisateur(nom string, age int) { fmt.Println("Bonjour", nom) if age > 18 { fmt.Println("Accès autorisé") }}
Bon exemple :
func saluer(nom string) { fmt.Println("Bonjour", nom)}
func autoriser(age int) { if age > 18 { fmt.Println("Accès autorisé") }}
2. Nommez vos fonctions clairement
Un bon nom doit exprimer l’intention. Utilisez des verbes pour les fonctions qui agissent, et des noms clairs pour celles qui renvoient une information.
// ❌ Mauvaisfunc f1() {}func traite() {}
// ✅ Bonfunc envoyerEmail() {}func estAutorisé(age int) bool {}
3. Limitez le nombre de paramètres
Essayez de ne pas dépasser 3 à 4 paramètres. Au-delà, pensez à regrouper les données dans une structure.
type Utilisateur struct { Nom string Age int}
func traiter(u Utilisateur) { // ...}
4. Évitez les effets de bord inattendus
Une fonction ne devrait pas modifier l’état global sans que ce soit clairement indiqué. Préférez les fonctions pures qui retournent une valeur sans changer l’environnement.
5. Gérer les erreurs explicitement
En Go, on retourne les erreurs sous forme de error
, il faut donc les gérer
proprement, sans les ignorer.
résultat, err := diviser(10, 0)if err != nil { log.Fatal(err)}
6. Commentez ce qui n’est pas évident
Pas besoin de commenter chaque ligne, mais si une logique est un peu complexe, expliquez-la. Et surtout, commentez chaque fonction exportée.
Exercice
L’objectif de cet exercice est d’écrire une fonction en Go qui calcule la moyenne de plusieurs nombres entiers. C’est un excellent moyen de pratiquer les fonctions variadiques, la gestion des types et l’organisation du code.
Ce que doit contenir le programme :
- Une fonction nommée
moyenne
: Elle doit accepter un nombre variable d’entiers (int
) et retourner la moyenne sous forme defloat64
. - Une fonction
main()
: Elle appellemoyenne
avec plusieurs jeux de données. - Une gestion des cas limites : Si aucun argument n’est passé, la fonction
doit retourner
0.0
. - Des affichages clairs : Affichez la moyenne calculée à chaque appel.
Commencez par créer un fichier main.go
, puis suivez les étapes ci-dessous.
En-tête du programme et importation des packages
package main
import ( "fmt")
Écriture de la fonction moyenne
Voici une fonction variadique qui accepte n’importe quel nombre d’entiers.
// moyenne calcule la moyenne des entiers donnés en paramètre.// Elle retourne 0.0 si aucun entier n'est fourni.func moyenne(valeurs ...int) float64 { // Si aucune valeur n'est fournie, on retourne 0.0 if len(valeurs) == 0 { return 0.0 }
// Initialisation d'une variable pour accumuler la somme total := 0
// Parcours de toutes les valeurs fournies en argument for _, v := range valeurs { total += v // On ajoute chaque valeur à la somme }
// On convertit la somme et le nombre de valeurs en float64 // pour éviter une division entière (et obtenir un résultat décimal) return float64(total) / float64(len(valeurs))}
Fonction principale pour tester plusieurs cas
Ajoutez un main()
qui utilise la fonction avec différents groupes de nombres.
func main() { fmt.Println("Moyenne de 10, 20, 30 :", moyenne(10, 20, 30)) fmt.Println("Moyenne de 5, 5, 5, 5 :", moyenne(5, 5, 5, 5)) fmt.Println("Moyenne sans argument :", moyenne())}
Exécution du programme
Enregistrez le fichier puis exécutez-le dans le terminal avec :
go run main.go
Résultat attendu
Le programme affichera :
Moyenne de 10, 20, 30 : 20Moyenne de 5, 5, 5, 5 : 5Moyenne sans argument : 0
Améliorations possibles
- Ajoutez une fonction
somme
pour séparer les responsabilités. - Acceptez des entrées utilisateur avec
fmt.Scanln
pour tester dynamiquement. - Gérez les cas d’erreur si on souhaite intégrer la lecture depuis une chaîne ou un fichier.
Cet exercice vous permet de bien comprendre comment déclarer une fonction en Go, manipuler des paramètres variables et travailler avec des types numériques.
Conclusion
Voilà, vous avez maintenant une bonne compréhension des fonctions en Go. Elles sont un outil puissant pour structurer votre code, le rendre réutilisable et surtout, plus lisible. En suivant les bonnes pratiques et en documentant vos fonctions, vous vous assurez que votre code sera maintenable et compréhensible par vous-même et par les autres développeurs.