Go a deux façons de déclarer une variable : var, qui fonctionne partout, et :=, une forme courte qui fonctionne dans les fonctions et déduit le type.
Chaque variable a un type fixe décidé à la compilation. Quand vous donnez une valeur, Go en déduit le type, donc écrire aussi le type (var city string = "Lisbon") est légal mais redondant.
var ou :=
var | := | |
|---|---|---|
| Où | au niveau du package et dans les fonctions | uniquement dans les fonctions |
| Type | facultatif ; obligatoire sans valeur | toujours déduit |
| Valeur initiale | facultative (valeur zéro sinon) | obligatoire |
| Redéclaration | jamais | permise si au moins une variable à gauche est nouvelle |
Le choix idiomatique dans une fonction :
x := valuequand vous avez une valeur de départ.var x Tquand vous voulez la valeur zéro, ce qui signale « vide exprès » :var buf bytes.Buffer,var total int.var x T = valuequand le type déduit n'est pas celui que vous voulez, par exemplevar ratio float64 = 1(un simpleratio := 1serait unint).
:= hors d'une fonction est une erreur de syntaxe :
./main.go:5:1: syntax error: non-declaration statement outside function body
Valeurs zéro
Une variable déclarée sans valeur ne contient jamais n'importe quoi. Elle contient la valeur zéro de son type :
| Type | Valeur zéro |
|---|---|
| entiers, flottants | 0 |
bool | false |
string | "" |
| pointeurs, slices, maps, channels, fonctions, interfaces | nil |
| tableaux, structs | chaque élément ou champ à sa valeur zéro |
Les bonnes API Go sont conçues pour que la valeur zéro soit utile : un sync.Mutex à zéro est déverrouillé, un bytes.Buffer à zéro est vide et prêt, et len, range et append fonctionnent tous sur une slice nil. Celle à surveiller est la map : lire une map nil renvoie des valeurs zéro, mais y écrire provoque un panic. Créez les maps avec make ou un littéral avant d'y stocker quoi que ce soit.
Déclarer plusieurs variables
Le bloc var ( ... ) regroupe des variables liées au niveau du package. Dans une affectation multiple comme x, y = y, x, chaque expression à droite est évaluée avant toute affectation, c'est pourquoi l'échange fonctionne.
:= avec plusieurs noms ne redéclare rien tant qu'au moins un nom à gauche est nouveau. Les noms existants dans la même portée sont simplement affectés. C'est ce qui permet de réutiliser err :
f, err := os.Open("a.txt") // declares f and err
g, err := os.Open("b.txt") // declares g, assigns to the existing err
Si aucun des noms n'est nouveau, c'est une erreur :
./main.go:7:4: no new variables on left side of :=
Utilisez un simple = pour affecter une variable qui existe déjà.
Les variables inutilisées sont des erreurs
Une variable locale déclarée mais jamais lue arrête le build :
./main.go:9:2: declared and not used: x
Ce n'est pas un avertissement que l'on peut désactiver. Supprimez la variable, utilisez-la, ou, pendant le débogage, faites-la taire avec l'identifiant blanc : _ = x. L'identifiant blanc sert aussi à jeter les valeurs de retour dont vous n'avez pas besoin : _, err := fmt.Println("hi").
Les variables au niveau du package et les paramètres de fonction échappent à cette règle.
Portée et masquage
Une variable existe de sa déclaration jusqu'à la fin du bloc englobant ({ ... }). Un := dans un bloc intérieur crée une nouvelle variable, même si une variable du même nom existe à l'extérieur. Celle de l'extérieur est masquée, pas modifiée :
Le programme affiche inside: 42 puis outside: 0. Le := à l'intérieur du if a déclaré count et err comme nouvelles variables dans le bloc intérieur, parce qu'aucune n'y existait encore. Cela compile sans problème et c'est une source classique de bugs du genre « ma valeur a disparu ».
La correction consiste à déclarer err d'abord et à affecter avec = :
var err error
count, err = strconv.Atoi(input)
go vet ne signale pas le masquage par défaut. L'analyseur séparé shadow le fait, et des linters comme golangci-lint peuvent l'activer.
Nommage
- Utilisez le
camelCase:maxRetries,userID. Pasmax_retries. - Gardez les acronymes dans une seule casse :
userID,httpClient,parseURL, pasuserId. - Des noms courts pour des portées courtes :
idans une boucle,rpour un reader,errpour une erreur. Des noms plus longs et descriptifs pour les variables au niveau du package. - Un nom en majuscule au niveau du package (
var Timeout) est exporté vers les autres packages. Voir packages et imports.
Variables au niveau du package
Les variables déclarées hors des fonctions vivent pendant tout le programme et sont initialisées avant l'exécution de main, dans l'ordre des dépendances. Elles sont pratiques pour la configuration et les caches, et elles sont partagées par toutes les goroutines. Si plus d'une goroutine y écrit, protégez-la avec un mutex ou utilisez sync/atomic, sinon vous avez une data race.
Préférez les constantes pour les valeurs qui ne changent jamais. Une const ne peut pas être modifiée par accident et ne coûte rien à l'exécution.
Questions fréquentes
Quelle est la différence entre var et := en Go ?
:= déclare et initialise une variable avec un type déduit, et ne fonctionne que dans les fonctions : count := 10. var fonctionne partout (y compris au niveau du package), permet d'indiquer le type, et peut omettre la valeur initiale pour obtenir la valeur zéro : var count int. Dans les fonctions, le Go idiomatique utilise := quand il y a une valeur initiale et var quand on veut la valeur zéro.
Que sont les valeurs zéro en Go ?
Toute variable déclarée sans valeur reçoit la valeur zéro de son type : 0 pour les nombres, false pour bool, "" pour string, et nil pour les pointeurs, slices, maps, channels, fonctions et interfaces. Les structs reçoivent la valeur zéro de chaque champ. Go n'a pas de variables non initialisées.
Comment corriger « declared and not used » en Go ?
Go refuse une variable locale qui est affectée mais jamais lue. Utilisez-la, supprimez-la, ou, pendant le débogage, affectez-la à l'identifiant blanc avec _ = x. Les variables au niveau du package et les paramètres de fonction ne sont pas concernés.
Comment déclarer une variable globale en Go ?
Utilisez var au niveau du package, hors de toute fonction : var counter int. La forme courte := n'y est pas permise. Un nom en majuscule (var Counter int) est visible par les autres packages ; un nom en minuscule n'est visible que dans son propre package. Protégez les variables au niveau du package avec un mutex si des goroutines y écrivent.