Objectifs
- Comprendre ce qu’est une exception et comment elle interrompt le flux d’un programme.
- Intercepter des exceptions spécifiques avec les blocs
beginetrescue. - Garantir l’exécution d’un code de nettoyage avec le bloc
ensure. - Déclencher ses propres erreurs avec
raise.
Un programme robuste doit être capable de gérer les situations inattendues. En Ruby, les erreurs sont gérées via un mécanisme d’exceptions.
Qu’est-ce qu’une exception ?
Une exception est un objet qui signale qu’une erreur s’est produite. Par exemple :
- Essayer de diviser par zéro (
ZeroDivisionError). - Essayer d’appeler une méthode sur
nil(NoMethodError). - Essayer d’ouvrir un fichier qui n’existe pas (
Errno::ENOENT).
Si une exception est “levée” (raised), et qu’elle n’est pas “attrapée” (rescued), le programme s’arrête brutalement et affiche un message d’erreur.
Intercepter les exceptions : begin et rescue
C’est l’équivalent du try...catch dans d’autres langages.
- Le code qui pourrait échouer est placé dans un bloc
begin. - Le code de gestion de l’erreur est placé dans un bloc
rescue.
begin
# On tente une opération risquée
puts "Tentative de division par zéro..."
resultat = 10 / 0
puts "Cette ligne ne sera jamais atteinte."
rescue
# Ce bloc est exécuté si une erreur se produit dans le 'begin'
puts "Oups ! Une erreur s'est produite."
end
puts "Le programme continue après le bloc."
Intercepter une exception spécifique
Il est de bonne pratique d’être précis sur le type d’erreur que l’on veut intercepter. Cela évite de masquer des bugs inattendus.
begin
puts "Tentative de division..."
resultat = 10 / 0
rescue ZeroDivisionError => e
# On intercepte SEULEMENT les erreurs de division par zéro
# La variable 'e' contient l'objet de l'exception
puts "Erreur interceptée : #{e.message}"
rescue TypeError => e
# On pourrait intercepter un autre type d'erreur ici
puts "Erreur de type : #{e.message}"
end
Le bloc ensure : Le nettoyage garanti
Le code placé dans un bloc ensure est toujours exécuté, qu’il y ait eu une exception ou non. C’est l’endroit parfait pour s’assurer que des ressources sont bien libérées (comme fermer un fichier ou une connexion réseau).
fichier = nil
begin
fichier = File.open("mon_fichier.txt", "w")
fichier.puts "Hello"
raise "Une erreur imprévue !" # On simule une erreur
rescue => e
puts "Erreur gérée : #{e.message}"
ensure
# Ce bloc s'exécute toujours
puts "Dans le bloc 'ensure'."
if fichier
fichier.close
puts "Fichier fermé."
end
end
(Rappel : File.open avec un bloc gère déjà cela automatiquement, mais c’est un bon exemple du principe de ensure).
Déclencher ses propres erreurs : raise
Vous pouvez vous-même lever des exceptions avec la commande raise.
def set_age(age)
if age < 0
raise "L'âge ne peut pas être négatif."
end
@age = age
end
begin
set_age(-5)
rescue => e
puts "Erreur : #{e.message}" # => "Erreur : L'âge ne peut pas être négatif."
end
Bonnes pratiques
- Soyez spécifique : Interceptez les erreurs les plus précises possible (
rescue ZeroDivisionError), et non la classeExceptiongénérique. - Ne sauvez pas de tout : N’utilisez pas
rescuepour masquer des erreurs de logique dans votre programme. Il sert à gérer des situations exceptionnelles et externes (une connexion réseau qui coupe, un fichier manquant…). - Utilisez
ensurepour garantir le nettoyage des ressources.
Exercices
-
Division par zéro :
- Écrivez un script qui demande deux nombres à l’utilisateur et affiche le résultat de leur division.
- Utilisez
begin...rescuepour intercepter laZeroDivisionErroret afficher un message d’erreur clair si l’utilisateur entre0comme deuxième nombre.
-
Lecture de fichier robuste :
- Créez une méthode qui prend un nom de fichier en argument et qui essaie de lire son contenu.
- Utilisez
rescuepour gérer le cas où le fichier n’existe pas (l’exception estErrno::ENOENT) en affichant “Le fichier n’a pas été trouvé.”
-
Nettoyage garanti :
- Modifiez le script de l’exercice 2.
- Ajoutez un bloc
ensurequi affiche toujours “Fin de la tentative de lecture.” à la fin, que le fichier ait été trouvé ou non.