Lesson 5/8 7 min

Functions and errors

Learn Go functions, multiple returns, explicit error handling, defer, panic and recover.

FR EN

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.

Une fonction Go renvoie souvent deux valeurs à la fois : le résultat et une erreur, récupérés d'un coup dans deux variables. diviser(10, 3) float64 error resultat = 3.33 err = nil
Deux valeurs renvoyées d'un coup : le résultat ET l'erreur. 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.

A Go function often returns two values at once: the result and an error, both unpacked into two variables. divide(10, 3) float64 error result = 3.33 err = nil
Two values returned at once: the result AND the error. 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 dans le Go Playground

Testez la fonction diviser et essayez de diviser par zéro :

Avec l'IA

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.
Ré-explique sans regarder

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 ?

Une bonne explication dit : en Go l'erreur est une valeur retournée, pas un événement qui interrompt le flux. La fonction renvoie (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.
Accepter ou rejeter le code de l'IA

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)
À rejeter. Deux erreurs sont avalées : le _ 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.
Rappel libre

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 ?

Le pattern est 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.
Combien de valeurs une fonction Go peut-elle retourner ?
Comment Go gère-t-il les erreurs ?
Que fait le mot-clé defer ?
Quand utiliser panic en Go ?
Next step

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 →
Besoin d'un développeur pour votre projet ?

Réponse sous 24h · Sans engagement