Les fonctions
Une fonction en Go se déclare avec func. Simple, lisible, pas de surprises :
func saluer(nom string) string {
return "Bonjour, " + nom + " !"
}
func main() {
message := saluer("Alice")
fmt.Println(message)
}
Remarquez que le type de retour (string) est déclaré après les paramètres, pas avant comme en C ou Java. C'est un choix de design de Go pour améliorer la lisibilité.
Sortie attendue :
Bonjour, Alice !
Retours multiples : la force de Go
En Go, une fonction peut retourner plusieurs valeurs. C'est là que Go brille par rapport aux autres langages :
func diviser(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division par zéro")
}
return a / b, nil
}
func main() {
resultat, err := diviser(10, 3)
if err != nil {
fmt.Println("Erreur:", err)
return
}
fmt.Println(resultat) // 3.3333333333333335
}
Le pattern valeur, err := fonction() est le cœur de Go. Vous le verrez partout.
resultat, err := diviser(10, 3).Pas de try/catch : et c'est voulu
Si vous venez de JavaScript ou Python, vous connaissez try/catch. Go fait un choix radicalement différent : pas d'exceptions. Chaque erreur est une valeur retournée que vous devez gérer explicitement.
file, err := os.Open("config.json")
if err != nil {
fmt.Println("Impossible d'ouvrir le fichier:", err)
return
}
defer file.Close()
// Utiliser file...
Ce pattern if err != nil peut sembler répétitif au début. Mais il force le développeur à penser à chaque erreur possible. Résultat : les programmes Go plantent rarement en production.
La philosophie de Go : "Les erreurs sont des valeurs, pas des événements exceptionnels."
defer, panic, recover
defer repousse l'exécution d'une instruction à la fin de la fonction. Idéal pour le nettoyage :
func lireFichier(nom string) {
file, err := os.Open(nom)
if err != nil {
fmt.Println(err)
return
}
defer file.Close() // Sera exécuté à la fin, quoi qu'il arrive
// Lire le fichier...
fmt.Println("Fichier ouvert avec succès")
}
panic et recover existent pour les situations vraiment catastrophiques (bug dans le programme, pas une erreur utilisateur) :
func safe() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Récupéré:", r)
}
}()
panic("quelque chose de terrible")
}
// Affiche: Récupéré: quelque chose de terrible
Règle d'or : utilisez error pour les erreurs normales, panic uniquement pour les bugs.
À vous d'essayer :
package main
import "fmt"
func diviser(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division par zéro")
}
return a / b, nil
}
func main() {
resultat, err := diviser(10, 3)
if err != nil {
fmt.Println("Erreur:", err)
return
}
fmt.Println(resultat)
}
Functions
A Go function is declared with func. Simple, readable, no surprises:
func greet(name string) string {
return "Hello, " + name + "!"
}
func main() {
message := greet("Alice")
fmt.Println(message)
}
Notice the return type (string) is declared after the parameters, not before like in C or Java. This is a Go design choice to improve readability.
Expected output:
Hello, Alice!
Multiple returns — Go's strength
In Go, a function can return multiple values. This is where Go shines compared to other languages:
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 3)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(result) // 3.3333333333333335
}
The value, err := function() pattern is the heart of Go. You'll see it everywhere.
result, err := divide(10, 3).No try/catch — and that's on purpose
If you come from JavaScript or Python, you know try/catch. Go makes a radically different choice: no exceptions. Every error is a returned value you must handle explicitly.
file, err := os.Open("config.json")
if err != nil {
fmt.Println("Cannot open file:", err)
return
}
defer file.Close()
// Use file...
This if err != nil pattern may seem repetitive at first. But it forces the developer to think about every possible error. Result: Go programs rarely crash in production.
Go's philosophy: "Errors are values, not exceptional events."
defer, panic, recover
defer postpones the execution of a statement until the end of the function. Perfect for cleanup:
func readFile(name string) {
file, err := os.Open(name)
if err != nil {
fmt.Println(err)
return
}
defer file.Close() // Will be executed at the end, no matter what
// Read the file...
fmt.Println("File opened successfully")
}
panic and recover exist for truly catastrophic situations (bug in the program, not a user error):
func safe() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered:", r)
}
}()
panic("something terrible")
}
// Prints: Recovered: something terrible
Golden rule: use error for normal errors, panic only for bugs.
Testez la fonction diviser et essayez de diviser par zéro :
Copiez ce prompt dans Claude ou ChatGPT :
Écris une fonction Go qui lit un fichier JSON et retourne son contenu dans un struct. Gère toutes les erreurs possibles (fichier introuvable, JSON invalide). Explique chaque if err != nil.
Sans relire la réponse de l'IA : avec tes mots, pourquoi chaque appel comme os.Open est suivi d'un if err != nil au lieu d'un try/catch ?
(résultat, error) et c'est l'appelant qui décide quoi faire via if err != nil. Avantage : l'erreur est visible dans la signature et impossible à oublier silencieusement ; inconvénient apparent : c'est verbeux. Le compilateur n'oblige pas à lire err, mais l'ignorer (_) est un anti-pattern.L'IA te propose ce code pour lire un fichier de config. Ton rôle de relecteur : l'accepter tel quel ou le rejeter, et dire pourquoi.
data, _ := os.ReadFile("config.json")
var cfg Config
json.Unmarshal(data, &cfg)
fmt.Println(cfg.Port)
_ jette l'erreur de os.ReadFile (fichier introuvable ? droits ?) et l'erreur de json.Unmarshal n'est même pas récupérée (JSON invalide passe inaperçu). Si la lecture échoue, data est vide et cfg.Port vaudra silencieusement zéro : un bug fantôme en production. Le réflexe Go : if err != nil { return err } après chaque appel faillible. C'est exactement le piège que la philosophie « les erreurs sont des valeurs » sert à éviter.Sans remonter dans la leçon : quel est le pattern classique d'appel d'une fonction Go qui peut échouer, et que met-on dans defer ?
résultat, err := fonction() suivi immédiatement de if err != nil { ... return } : on récupère la valeur ET l'erreur, puis on traite l'erreur avant de continuer. Dans defer on met le nettoyage à exécuter à la sortie de la fonction, quoi qu'il arrive : typiquement defer file.Close() juste après une ouverture réussie.Now that your functions return their errors cleanly, we tackle Go's real reason for existing: concurrency. You launch parallel tasks with the go keyword and make them talk through channels.
Lesson 6: Goroutines and channels →