Une fonction regroupe une logique réutilisable derrière un nom. Vous la déclarez
avec le mot-clé func, listez ses paramètres typés et indiquez les types
qu’elle retourne. Les fonctions sont la principale unité de réutilisation en Go,
et ce sont aussi des valeurs.
Déclarer une fonction
La liste des paramètres vient d’abord, puis le type de retour, puis le corps.
Utilisez return pour renvoyer une valeur.
func greet(name string) string {
return "Bonjour, " + name
}
Lorsque plusieurs paramètres partagent un type, écrivez le type une seule fois à la fin :
func add(a, b int) int {
return a + b
}
Les retours multiples
Une fonction Go peut retourner plusieurs valeurs. C’est utilisé en permanence, le plus souvent pour renvoyer un résultat accompagné d’un état.
func minMax(a, b int) (int, int) {
if a < b {
return a, b
}
return b, a
}
L’appelant reçoit les deux valeurs en un seul appel : lo, hi := minMax(9, 4).
Les retours nommés (avec parcimonie)
Vous pouvez nommer les valeurs de retour. Elles se comportent comme des
variables initialisées à leur valeur zéro, et un return nu renvoie leurs
valeurs courantes.
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
Les retours nommés peuvent documenter l’intention, mais un return nu dans une
longue fonction se lit mal. Préférez des retours explicites, sauf si les noms
apportent vraiment de la clarté.
Les fonctions variadiques
Un dernier paramètre écrit ...T accepte un nombre quelconque d’arguments, qui
arrivent sous forme de slice.
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
Appelez-la avec sum(1, 2, 3), ou déployez une slice existante avec
sum(values...).
Les fonctions comme valeurs et les closures
Les fonctions sont des valeurs de première classe : vous pouvez les stocker dans des variables et les retourner. Une fonction qui capture des variables de son environnement est une closure.
func counter() func() int {
count := 0
return func() int {
count++
return count
}
}
Chaque appel à counter() renvoie une nouvelle fonction avec son propre count
privé.
La convention result-error
Le Go idiomatique signale un échec en retournant une valeur et une error.
Par convention, l’error est la dernière valeur de retour et vaut nil en cas
de succès. L’appelant la vérifie avant d’utiliser le résultat.
package main
import (
"errors"
"fmt"
)
// divide retourne le quotient entier de a par b.
// Elle retourne une erreur lorsque b vaut zéro.
func divide(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division par zéro")
}
return a / b, nil
}
func main() {
if q, err := divide(10, 2); err == nil {
fmt.Println("10 / 2 =", q)
}
if _, err := divide(1, 0); err != nil {
fmt.Println("erreur :", err)
}
}
Son exécution affiche :
10 / 2 = 5
erreur : division par zéro
Erreurs fréquentes
- Ignorer l’erreur retournée. Si une fonction retourne
(T, error), vérifiez l’error; ne l’ignorez avec_que si vous êtes certain qu’elle ne peut pas survenir. - Retourner une valeur utile avec une erreur non nulle. En cas d’échec, retournez la valeur zéro pour que l’appelant ne soit pas tenté d’utiliser un résultat invalide.
- Abuser des retours nommés. Dans une longue fonction, un
returnnu cache ce qui est réellement renvoyé. Ne nommez les retours que si cela améliore la clarté.
À vous de jouer
Écrivez une fonction average(nums ...float64) float64 qui retourne la moyenne
de ses arguments, et 0 si elle est appelée sans argument. Affichez la moyenne
de 4, 8, 15, 16, 23, 42 avec deux décimales.
Afficher la correction
package main
import "fmt"
func average(nums ...float64) float64 {
if len(nums) == 0 {
return 0
}
var sum float64
for _, n := range nums {
sum += n
}
return sum / float64(len(nums))
}
func main() {
fmt.Printf("%.2f\n", average(4, 8, 15, 16, 23, 42))
}Le garde-fou sur le cas vide évite une division par zéro, et diviser la somme
par le nombre d’éléments donne 18.00.
Résumé
- Déclarez les fonctions avec
func, des paramètres typés et des retours typés. - Les retours multiples portent la convention
(result, error). - Les paramètres variadiques et les closures rendent les fonctions flexibles ; gardez-les courtes.
Ensuite, explorez l’idiome qui façonne les API Go avec la gestion des erreurs, et revenez sur la manière de stocker des valeurs dans les variables et types.