La portée des variables dans le langage de programmation Go

Ce chapitre vous explique les portée des variables en GoLang. Vous allez apprendre à différencier les différents types de portée à savoir : les variables locales, variables globales et paramètres formels.

Présentation

Quand on parle de portée des variables on parle d’endroits dans notre code où on peut utiliser telle ou telle variable.

On peut résumer la portée des variables par la question suivante :

"Est-ce que ma variable est accessible dans tel ou tel bloc de mon code ?"

On peut différencier la portée de nos variables par des :

  • Variables locales
  • Variables globales
  • Paramètres formels

Variables locales

Les variables déclarées à l'intérieur d'une fonction ou d'un bloc (un bloc est tout simplement la partie de votre code dans des accolades) sont appelées variables locales. Elles ne peuvent être accessibles qu’à l’intérieur de votre fonction ou d'un bloc de code.

Exemple d'une variable accessible que depuis un bloc :

package main

import (
    "fmt"
)

func test() {
    for i := 1; i < 3; i++ {
        a := 20
        a *= i
        fmt.Println(a)
    }
}

func main() {
    test()
}

Résultat :

20
40

Jusqu’ici rien de choquant par contre si on tente de manipuler la variable a en dehors de notre bloc …

package main

import (
    "fmt"
)

func test() {
    for i := 1; i < 3; i++ {
        a := 20
        a *= i
        fmt.Println(a)
    }
    fmt.Println(a) // erreur ici
}

func main() {
    test()
}

Erreur :

undefined: a

Si on souhaite exploiter notre variable à la fois dans notre fonction et à la fois dans notre bloc alors il suffit de la déclarer au début de notre fonction, comme ceci

package main

import (
    "fmt"
)

func test() {
    a := 20 // déclaration de notre variable locale
    for i := 1; i < 3; i++ {
        a *= i
        fmt.Println("dans ma boucle for :", a)
    }
    fmt.Println("en dehors de ma boucle for :", a)
}

func main() {
    test()
}

Résultat :

dans ma boucle for : 20
dans ma boucle for : 40
en dehors de ma boucle for : 40

On va maintenant tenter d’utiliser une variable en dehors d'une fonction

package main

import (
    "fmt"
)

func test() {
    a := 10
    a += 20
    fmt.Println(a)
}
func main() {
    test()
    fmt.Println(a) // l'erreur vient d'ici
}

Erreur :

undefined: a

Hum ça ne fonctionne pas, il serait peut-être temps de voir comment ça se passe au niveau des variables globales.

Variables globales

Les variables globales sont définies en dehors de vos fonctions (généralement au début de votre programme). À l'inverse des variables locales elles conservent leur valeur pendant toute la durée de vie du programme et sont accessibles à l’intérieur de n’importe quelles fonctions définies dans votre programme.

Ça tombe bien car ça va résoudre notre problème !

package main

import (
    "fmt"
)

var g int // déclaration de notre variable globale

func test() {
    g += 20
    fmt.Println("Pendant ma fonction test() : ", g)
}
func main() {
    fmt.Println("Avant l'utilisation de la fonction test() :", g)
    test()
    fmt.Println("Pendant ma fonction main() : ", g)
    g += 30
    fmt.Println("Modifie moi encore : ", g)
}

Résultat :

Avant l'utilisation de la fonction test() : 0
Pendant ma fonction test() :  20
Pendant ma fonction main() :  20
Modifie moi encore :  50

Avertissement

Comme vous vous pouvez le constater la modification d'une variable globale est permanente quel que soit l'endroit où elle est modifiée.

Paramètres formels

Les paramètres formels sont traités comme des variables locales dans une fonction par contre ils auront toujours une priorité sur les variables globales.

package main

import (
    "fmt"
)

var g int // déclaration de notre variable formel

func test(g int) { // déclaration de notre paramètre globale
    g += 20 // prend le dessus sur notre variable globale
    fmt.Println("Pendant ma fonction test() : ", g)
}
func main() {
    fmt.Println("Avant l'utilisation de la fonction test() :", g)
    test(20)
    fmt.Println("Pendant ma fonction main() : ", g)
    g += 30
    fmt.Println("Modifie moi encore : ", g)
}

Résultat :

Avant l'utilisation de la fonction test() : 0
Pendant ma fonction test() :  40
Pendant ma fonction main() :  0
Modifie moi encore :  30

Espace commentaire

Écrire un commentaire

Rejoignez la discussion

Vous devez être connecté pour poster un message.

28 commentaires

ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

D'ailleurs, si vous avez des doutes sur la portée, lancez go vet sur vos sources. Ça détecte souvent les shadowing de variables qui causent ces erreurs de logique.

31/03/2019 à 16:42
ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

C'est le piège classique. L'opérateur := crée une nouvelle variable si elle n'existe pas dans le bloc courant. Utilise = si tu veux juste assigner une valeur à une variable déjà déclarée.

31/03/2019 à 11:38
isabelle12
Membre Actif
Avatar de isabelle12
isabelle12
Membre Actif

Je galère avec les short variable declaration (:=) dans mes blocs, ça écrase mes variables de portée supérieure parfois.

31/03/2019 à 04:56
ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

Oui, avec un pointeur tu modifies l'adresse mémoire directement. Tu peux donc altérer la valeur originale même avec un nom de paramètre identique. Mais c'est une très mauvaise pratique, ne fais pas ça.

30/03/2019 à 21:52
vallee-georges
Membre Actif
Avatar de vallee-georges
vallee-georges
Membre Actif

J'ai testé l'exemple sur les paramètres formels. Si je passe un pointeur, ça change la donne pour la modification de la globale ?

30/03/2019 à 16:32
ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

La variable doit commencer par une majuscule pour être exportée. Si elle est en minuscule, elle est privée au package. C'est la base de l'encapsulation en Go.

30/03/2019 à 09:37
richard13
Membre Actif
Avatar de richard13
richard13
Membre Actif

Comment je peux accéder à une variable d'un autre package ?

30/03/2019 à 04:23
ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

Oui, Go est très strict. Si tu déclares une variable et que tu ne l'utilises jamais dans ton code, le compilateur va te bloquer. C'est une sécurité pour éviter le code mort.

29/03/2019 à 22:17
laurence-bertin
Membre Actif
Avatar de laurence-bertin
laurence-bertin
Membre Actif

J'ai une erreur bizarre, mon IDE me dit que ma variable est inutilisée alors qu'elle est globale. C'est normal ?

29/03/2019 à 16:49
ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

Vérifie que t'as bien tous tes fichiers dans le même package. Si tu as plusieurs fichiers, fais un go run . au lieu de juste go run main.go.

29/03/2019 à 09:58
ibailly
Membre Actif
Avatar de ibailly
ibailly
Membre Actif

Le code compile pas sur mon Windows, j'ai undefined sur ma variable globale alors que j'ai bien mis mon main.go.

29/03/2019 à 03:50
ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

Oui, sauf que la constante ne peut pas être réassignée. La portée est la même, au niveau du fichier ou du package.

28/03/2019 à 22:02
qrodriguez
Membre Actif
Avatar de qrodriguez
qrodriguez
Membre Actif

J'ai essayé de définir une constante globale, est-ce que ça suit les mêmes règles que les variables ?

28/03/2019 à 17:45
ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

Non, tant que tu les appelles via le nom du package, style pkg1.Var et pkg2.Var. La portée est limitée au package.

28/03/2019 à 12:52
hardy-elise
Membre Actif
Avatar de hardy-elise
hardy-elise
Membre Actif

Moi j'ai un souci, j'ai une variable qui porte le même nom dans deux packages différents. Ça pose problème ?

28/03/2019 à 08:11
ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

Absolument pas. Si plusieurs goroutines tapent dedans en même temps, tu vas droit vers un crash ou des données corrompues. Utilise des sync.Mutex si tu n'as pas le choix.

28/03/2019 à 00:11
corinne86
Membre Actif
Avatar de corinne86
corinne86
Membre Actif

Est-ce que les variables globales sont thread-safe ?

27/03/2019 à 19:10
celina-pottier
Membre Actif
Avatar de celina-pottier
celina-pottier
Membre Actif

Top le tuto. J'ai galéré avec les portées dans mes boucles, j'essayais de modifier une variable extérieure sans succès avant de lire ton exemple avec a := 20.

27/03/2019 à 12:06
ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

Tu ne peux pas faire d'assignation directe en dehors d'une fonction. Utilise var pour déclarer et laisse le package init() pour l'initialisation :

var config string
func init() {
    config = "prod"
}
27/03/2019 à 07:33
lblin
Membre Actif
Avatar de lblin
lblin
Membre Actif

J'ai une erreur non-declaration statement outside function body quand je tente d'assigner une valeur à ma globale. Comment on fait pour initialiser ça proprement ?

27/03/2019 à 00:38
ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

En Go, on évite au maximum les variables globales pour les tests unitaires et la concurrence. C'est pratique pour des flags simples ou des configs, mais à ne jamais utiliser pour gérer un état partagé si tu veux pas de race conditions.

26/03/2019 à 18:20
daniel-alphonse
Membre Actif
Avatar de daniel-alphonse
daniel-alphonse
Membre Actif

Merci pour l'article, c'est clair. Par contre, pour les variables globales, c'est vraiment recommandé en prod ? J'ai cru comprendre que c'est le mal incarné.

26/03/2019 à 13:52
ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

Exact. Le bloc if crée sa propre portée. Si tu veux y accéder après, tu dois la déclarer avant avec var :

var result int
if true {
    result = 10
}
fmt.Println(result)
26/03/2019 à 09:11
tboulanger
Membre Actif
Avatar de tboulanger
tboulanger
Membre Actif

Je bosse sur un projet Go, j'ai tenté de définir une variable dans un if et de la réutiliser après. Ça passe pas. Je suis obligé de la déclarer avant le if ?

26/03/2019 à 01:46
ajdaini-hatim
Auteur Rédacteur Secouriste Actif
Avatar de ajdaini-hatim
ajdaini-hatim
Auteur Rédacteur Secouriste Actif

C'est le comportement standard. Si tu nommes ton paramètre comme ta globale, le compilateur privilégie la portée locale. Ne jamais nommer tes paramètres comme tes globales, c'est la règle d'or pour éviter de s'arracher les cheveux.

25/03/2019 à 20:26

Rejoindre la communauté

Recevoir les derniers articles gratuitement en créant un compte !

S'inscrire