Aller au contenu

Ecrire des fonctions en GO

Mise à jour :

Go

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()) // 1
fmt.Println(compte()) // 2
fmt.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.

// ❌ Mauvais
func f1() {}
func traite() {}
// ✅ Bon
func 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 :

  1. Une fonction nommée moyenne : Elle doit accepter un nombre variable d’entiers (int) et retourner la moyenne sous forme de float64.
  2. Une fonction main() : Elle appelle moyenne avec plusieurs jeux de données.
  3. Une gestion des cas limites : Si aucun argument n’est passé, la fonction doit retourner 0.0.
  4. Des affichages clairs : Affichez la moyenne calculée à chaque appel.

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.