Menu

Fonctions en Golang : déclarer, appeler et passer des fonctions

Comment déclarer et appeler des fonctions en Go : paramètres, types de paramètres partagés, valeurs de retour, passage par valeur, et fonctions en tant que valeurs que l'on stocke et que l'on passe.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Déclarer une fonction

Une déclaration de fonction se compose de func, d'un nom, des paramètres, du type du résultat et d'un corps. Le type se place après le nom du paramètre, pas avant.

add prend deux valeurs int et renvoie un int. greet ne renvoie rien, donc sa signature n'a pas de type de résultat et son corps n'a pas besoin de return.

Une fonction qui déclare un résultat doit se terminer par une instruction terminale. Si un chemin peut atteindre l'accolade fermante sans retourner, le compilateur s'arrête avec missing return.

Des paramètres qui partagent un type

Des paramètres consécutifs du même type peuvent partager un seul nom de type. func add(a, b int) int signifie exactement la même chose que func add(a int, b int) int. Le mélange est aussi permis :

func scale(x, y float64, factor int) (float64, float64)

Ici x et y sont des float64 et factor est un int.

Valeurs de retour

Une fonction peut renvoyer zéro, une ou plusieurs valeurs. Plusieurs résultats se mettent entre parenthèses, et l'appelant les reçoit par une affectation multiple :

Ce code affiche 3 2. La paire (value, error) est l'usage le plus courant de cette fonctionnalité en Go, et elle a sa propre page sur les valeurs de retour multiples.

Les arguments sont copiés

Go passe chaque argument par valeur. La fonction travaille sur une copie, donc affecter une valeur à un paramètre ne modifie jamais la variable de l'appelant.

La sortie est :

{1 2}
{100 2}
[99 2 3]

La troisième ligne surprend. Une valeur slice est un petit en-tête (pointeur, longueur, capacité). La copie pointe vers le même tableau sous-jacent, donc écrire s[0] écrit l'élément de l'appelant. Mais append dans la fonction peut allouer un nouveau tableau, et l'appelant ne voit jamais ce nouvel en-tête. Si une fonction agrandit une slice, renvoyez la nouvelle slice. La même logique s'applique aux maps : la fonction peut ajouter des clés que l'appelant voit, parce que la valeur map fait référence à des données partagées.

Utilisez un paramètre pointeur quand la fonction doit modifier la variable de l'appelant, ou quand la valeur est une grosse struct que vous ne voulez pas copier à chaque appel.

Les fonctions sont des valeurs

Une fonction a un type, écrit sans le nom : func(int, int) int. Vous pouvez stocker une fonction dans une variable, la mettre dans une map ou une slice, et la passer à une autre fonction.

square est une fonction anonyme affectée à une variable. Les fonctions anonymes peuvent aussi lire et modifier des variables de la portée environnante, ce qui en fait des closures.

Un type fonction nommé rend les signatures plus lisibles quand la même forme apparaît à de nombreux endroits :

type Transform func(int) int

func apply(xs []int, f Transform) []int

La valeur zéro d'un type fonction est nil. Appeler une fonction nil provoque un panic invalid memory address or nil pointer dereference, donc vérifiez les callbacks optionnels avant de les appeler. Les valeurs fonction ne peuvent être comparées qu'à nil, jamais entre elles.

Récursion

Une fonction peut s'appeler elle-même. Go n'a pas d'optimisation des appels terminaux, mais les piles des goroutines grandissent à la demande, donc des profondeurs de récursion ordinaires ne posent pas de problème.

for i := range 6 compte de 0 à 5 et demande Go 1.22 ou plus.

Ce que les fonctions Go n'ont pas

  • Pas de surcharge. Deux fonctions d'un même package ne peuvent pas partager un nom, même avec des paramètres différents.
  • Pas d'arguments par défaut ni nommés. Chaque paramètre est passé à chaque appel, dans l'ordre. Une struct de configuration vous donne des champs nommés et facultatifs : NewServer(Config{Port: 8080}).
  • Pas de fonctions nommées imbriquées. Dans le corps d'une fonction, vous ne pouvez déclarer que des fonctions anonymes, généralement affectées à une variable.

Les listes d'arguments de longueur variable sont prises en charge par les fonctions variadiques.

Fonctions exportées et non exportées

Une fonction dont le nom commence par une majuscule (Parse) est exportée et peut être appelée depuis d'autres packages. Un nom en minuscule (parse) n'est visible que dans son propre package. Il n'y a pas de mots-clés public ou private ; la casse est toute la règle.

Erreurs courantes

  • S'attendre à ce que la modification d'un paramètre atteigne l'appelant. Affecter une valeur à un paramètre ne modifie que la copie. Renvoyez la nouvelle valeur ou prenez un pointeur.
  • Faire un append dans une fonction et ignorer le résultat. func add(s []int) { s = append(s, 1) } n'a aucun effet sur la longueur de la slice de l'appelant. Écrivez func add(s []int) []int et utilisez s = add(s).
  • Des résultats ignorés. Go permet d'ignorer une valeur de retour. go vet ne signale pas une erreur ignorée, mais des linters comme errcheck (inclus dans golangci-lint) le font. N'utilisez _ = que lorsqu'ignorer est une décision.

Questions fréquentes

Comment définir une fonction en Go ?

Utilisez le mot-clé func, un nom, une liste de paramètres avec les types après les noms, et le type du résultat : func add(a, b int) int { return a + b }. Une fonction sans résultat omet le type, et une fonction avec plusieurs résultats les liste entre parenthèses : func divmod(a, b int) (int, int).

Go passe-t-il les arguments par valeur ou par référence ?

Toujours par valeur. La fonction reçoit une copie de chaque argument. Pour un int ou une struct, cette copie est indépendante de la variable de l'appelant. Les slices, maps, channels et pointeurs sont de petites valeurs qui font référence à des données partagées, donc les modifications des éléments pointés sont visibles par l'appelant, alors que réaffecter le paramètre lui-même ne l'est pas. Passez un pointeur (*T) quand la fonction doit modifier la variable de l'appelant.

Peut-on passer une fonction en paramètre en Go ?

Oui. Les fonctions sont des valeurs avec un type comme func(int) int. Déclarez un paramètre de ce type et passez n'importe quelle fonction de signature compatible, nommée ou anonyme : func apply(xs []int, f func(int) int).

Go supporte-t-il la surcharge de fonctions ou les paramètres par défaut ?

Non. Chaque nom de fonction d'un package doit être unique, et chaque paramètre doit être passé à chaque appel. Les substituts habituels sont des noms distincts (NewServer, NewServerWithTLS), une struct de configuration, les paramètres variadiques, ou les options fonctionnelles.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER