Leçon 2/11 7 min

Variables et types

Maîtrisez le typage statique de Go : var, :=, types primitifs, constantes, pointeurs et structs.

Déclarer des variables

En Go, il y a deux façons de déclarer une variable :

package main

import "fmt"

func main() {
    // Déclaration longue avec var
    var nom string = "Alice"
    var age int = 30

    // Déclaration courte avec := (la plus utilisée)
    ville := "Paris"
    score := 42.5

    fmt.Println(nom, age, ville, score)
}

La différence ? := est un raccourci qui laisse Go déduire le type automatiquement. Vous l'utiliserez 90 % du temps. var est utile quand vous voulez déclarer une variable sans l'initialiser ou au niveau du package (là où := n'est pas autorisé).

Attention : Go est strict. Si vous déclarez une variable sans l'utiliser, le compilateur refuse de compiler. Zéro gaspillage. Lancez le code ci-dessus et changez une valeur pour voir.

Les types de base

Go est un langage à typage statique : chaque variable a un type fixé à la compilation. Voici les types essentiels :

nom := "Bonjour"      // string : chaîne de caractères
age := 25              // int : nombre entier
prix := 19.99          // float64 : nombre décimal
actif := true          // bool : vrai ou faux

Contrairement à Python ou JavaScript, vous ne pouvez pas mettre un nombre dans une variable string. Le compilateur vous arrête avant que le bug n'arrive en production.

Go a aussi des types numériques plus précis : int8, int16, int32, int64, float32, float64. Pour débuter, int et float64 suffisent largement.

Constantes

Une constante est une valeur qui ne change jamais. En Go, on utilise const :

const Pi = 3.14159
const AppName = "MonApp"
const MaxRetries = 3

// Pi = 3.0  // Erreur ! On ne peut pas modifier une constante

Les constantes sont évaluées à la compilation, pas à l'exécution. Elles sont plus rapides et plus sûres que les variables.

Pointeurs : & et *

Un pointeur est une variable qui contient l'adresse mémoire d'une autre variable. Deux opérateurs :

Prédisez avant de lire

On a ce code : func doubler(n int) { n = n * 2 } puis x := 5; doubler(x). Avant de dérouler : après l'appel à doubler(x), que vaut x : 10 ou 5 ? Et comment écrire doubler pour qu'il modifie VRAIMENT la variable de l'appelant ?

Voir la réponse

x vaut toujours 5. En Go, les arguments sont passés par valeur : doubler reçoit une copie de x, et modifier cette copie (n = n * 2) ne touche pas l'original. Pour modifier la variable de l'appelant, on passe un pointeur : func doubler(n *int) { *n = *n * 2 }, appelé avec doubler(&x). &x donne l'adresse de x, et *n (déréférencement) lit et écrit la valeur à cette adresse. Après ça, x vaut 10. Le pointeur, c'est « voici OÙ se trouve ma variable », pas « voici une copie de sa valeur ».

age := 25
ptr := &age    // & = "donne-moi l'adresse de age"
fmt.Println(ptr)   // 0xc0000b4008 (une adresse mémoire)
fmt.Println(*ptr)  // 25 : * = "donne-moi la valeur à cette adresse"

*ptr = 30          // Modifier la valeur via le pointeur
fmt.Println(age)   // 30 : age a changé !
La variable ptr contient l'adresse de la variable age ; l'opérateur & donne l'adresse, l'opérateur * lit ou modifie la valeur à cette adresse. ptr 0xc0000b4008 age 25 → 30 &age pointe vers *ptr lit ou modifie la valeur de age via son adresse
& donne l'adresse, * accède à la valeur à cette adresse.

Pourquoi les pointeurs ? Quand vous passez une variable à une fonction, Go en fait une copie. Avec un pointeur, la fonction peut modifier la variable originale. C'est essentiel pour l'efficacité.

Structs : créer vos propres types

Un struct regroupe plusieurs champs en un seul type. C'est l'équivalent d'un objet en JavaScript ou d'une classe en Python :

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    alice := Person{
        Name: "Alice",
        Age:  30,
        City: "Lyon",
    }
    fmt.Println(alice.Name) // Alice
    fmt.Println(alice.Age)  // 30
}

Les structs sont la base de la programmation en Go. Pas de classes, pas d'héritage : juste des structs et des fonctions. Simple et efficace.

À vous d'essayer :

package main

import "fmt"

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    ville := "Paris"
    score := 42.5
    alice := Person{Name: "Alice", Age: 30, City: "Lyon"}

    fmt.Println(ville, score)
    fmt.Println(alice.Name, alice.Age, alice.City)
}

Declaring variables

In Go, there are two ways to declare a variable:

package main

import "fmt"

func main() {
    // Long declaration with var
    var name string = "Alice"
    var age int = 30

    // Short declaration with := (most common)
    city := "Paris"
    score := 42.5

    fmt.Println(name, age, city, score)
}

The difference? := is a shortcut that lets Go infer the type automatically. You'll use it 90% of the time. var is useful when you want to declare a variable without initializing it or at the package level (where := isn't allowed).

Warning: Go is strict. If you declare a variable without using it, the compiler refuses to compile. Zero waste. Run the code above and change a value to see it.

Basic types

Go is a statically typed language — every variable has a type fixed at compile time. Here are the essential types:

name := "Hello"        // string — text
age := 25              // int — integer
price := 19.99         // float64 — decimal number
active := true         // bool — true or false

Unlike Python or JavaScript, you can't put a number in a string variable. The compiler stops you before the bug reaches production.

Go also has more precise numeric types: int8, int16, int32, int64, float32, float64. For getting started, int and float64 are plenty.

Constants

A constant is a value that never changes. In Go, we use const:

const Pi = 3.14159
const AppName = "MyApp"
const MaxRetries = 3

// Pi = 3.0  // Error! You can't modify a constant

Constants are evaluated at compile time, not at runtime. They are faster and safer than variables.

Pointers: & and *

A pointer is a variable that holds the memory address of another variable. Two operators:

Predict before reading

Consider this code: func doubler(n int) { n = n * 2 } then x := 5; doubler(x). Before reading on: after the call to doubler(x), what is x: 10 or 5? And how would you rewrite doubler so it ACTUALLY modifies the caller's variable?

See the answer

x is still 5. In Go, arguments are passed by value: doubler receives a copy of x, and modifying that copy (n = n * 2) does not touch the original. To modify the caller's variable, pass a pointer: func doubler(n *int) { *n = *n * 2 }, called with doubler(&x). &x gives the address of x, and *n (dereferencing) reads and writes the value at that address. After that, x is 10. A pointer means “here is WHERE my variable lives”, not “here is a copy of its value”.

age := 25
ptr := &age    // & = "give me the address of age"
fmt.Println(ptr)   // 0xc0000b4008 (a memory address)
fmt.Println(*ptr)  // 25 — * = "give me the value at this address"

*ptr = 30          // Modify the value via the pointer
fmt.Println(age)   // 30 — age changed!
The variable ptr holds the address of the variable age; the & operator gives the address, the * operator reads or modifies the value at that address. ptr 0xc0000b4008 age 25 → 30 &age points to *ptr reads or modifies age's value via its address
& gives the address, * accesses the value at that address.

Why pointers? When you pass a variable to a function, Go makes a copy. With a pointer, the function can modify the original variable. This is essential for efficiency.

Structs: creating your own types

A struct groups multiple fields into a single type. It's the equivalent of an object in JavaScript or a class in Python:

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    alice := Person{
        Name: "Alice",
        Age:  30,
        City: "Lyon",
    }
    fmt.Println(alice.Name) // Alice
    fmt.Println(alice.Age)  // 30
}

Structs are the foundation of Go programming. No classes, no inheritance — just structs and functions. Simple and effective.

Try it yourself:

package main

import "fmt"

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    alice := Person{Name: "Alice", Age: 30, City: "Lyon"}
    fmt.Println(alice.Name)
    fmt.Println(alice.Age)
}

🎯 Pratique

S'entraîner (clique pour ouvrir) :

Prompt IA
Avec l'IA

Copiez ce prompt dans Claude ou ChatGPT :

Crée un struct Go "Product" avec un nom, un prix et un stock. Écris une fonction qui applique une réduction de 10% sur le prix. Utilise un pointeur.
💬 Ré-explique sans regarder
Ré-explique sans regarder

Sans relire la réponse de l'IA : pourquoi la fonction de réduction sur le Product a-t-elle besoin d'un pointeur ? Que ferait exactement & et * dedans ?

Une bonne explication dit : sans pointeur, Go passe une copie du Product à la fonction, donc baisser le prix ne touche que la copie ; l'original ne change pas. En prenant *Product en paramètre et en appelant avec &monProduit, la fonction reçoit l'adresse ; p.Price = ... (déréférencement implicite, équivalent de (*p).Price) modifie alors la struct d'origine.
⚖️ Juge le code de l'IA
Accepter ou rejeter le code de l'IA

L'IA te propose cette fonction de réduction. Ton rôle de relecteur : l'accepter telle quelle ou la rejeter, et dire pourquoi.

func applyDiscount(p Product) {
    p.Price = p.Price * 0.9
}

func main() {
    laptop := Product{Name: "Laptop", Price: 1000}
    applyDiscount(laptop)
    fmt.Println(laptop.Price) // 1000
}
À rejeter. Le paramètre est p Product (valeur), donc applyDiscount reçoit une copie : la ligne p.Price = p.Price * 0.9 modifie la copie, jamais le laptop d'origine, qui reste à 1000. Le code compile et ne plante pas, mais la réduction est silencieusement perdue : c'est le piège classique du passage par valeur en Go. Le correctif : func applyDiscount(p *Product) et appel applyDiscount(&laptop).
🧠 Rappel libre
Rappel libre

Sans remonter dans la leçon : quelle différence entre var nom string = "Alice" et nom := "Alice", et que fait le compilateur Go face à une variable déclarée mais jamais utilisée ?

:= est la déclaration courte : Go déduit le type tout seul, et elle ne marche qu'à l'intérieur d'une fonction. var est plus explicite (on peut écrire le type), accepte une déclaration sans valeur initiale et fonctionne aussi au niveau du package. Côté rigueur : une variable déclarée mais jamais utilisée provoque une erreur de compilation (pas un simple avertissement) ; Go refuse de compiler.
Quelle est la déclaration courte en Go ?
Que se passe-t-il si vous déclarez une variable sans l'utiliser en Go ?
Que fait l'opérateur & en Go ?
Prochaine étape

Vos variables et structs sont en place. On va leur donner vie avec des fonctions qui renvoient plusieurs valeurs, dont la fameuse paire (résultat, error) : vous découvrez pourquoi Go ignore try/catch et gère ses erreurs avec if err != nil, plus le mot-clé defer.

Leçon 3 : Conditions et boucles →

Une erreur dans cette leçon, un passage flou, une question ? Écrivez-moi : chaque retour améliore ce cours.

Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement