Recherche…


Un bloc de gestion des erreurs de base

Faisons une fonction pour diviser deux nombres, cela fait très confiance à ses entrées:

def divide(x, y)
  return x/y
end

Cela fonctionnera très bien pour beaucoup d'entrées:

> puts divide(10, 2)
5

Mais pas tout

> puts divide(10, 0)
ZeroDivisionError: divided by 0

> puts divide(10, 'a')
TypeError: String can't be coerced into Fixnum

Nous pouvons réécrire la fonction en encapsulant l'opération de division risquée dans un bloc begin... end pour vérifier les erreurs et utiliser une clause rescue pour générer un message et renvoyer nil s'il y a un problème.

def divide(x, y)
  begin
    return x/y
  rescue
    puts "There was an error"
    return nil
  end
end

> puts divide(10, 0)
There was an error

> puts divide(10, 'a')
There was an error

Enregistrement de l'erreur

Vous pouvez enregistrer l'erreur si vous souhaitez l'utiliser dans la clause de rescue

def divide(x, y)
  begin
    x/y
  rescue => e
    puts "There was a %s (%s)" % [e.class, e.message]
    puts e.backtrace
  end
end

> divide(10, 0)
There was a ZeroDivisionError (divided by 0)
       from (irb):10:in `/'
       from (irb):10
       from /Users/username/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'

> divide(10, 'a')
There was a TypeError (String can't be coerced into Fixnum)
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87:in `eval'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/workspace.rb:87:in `evaluate'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/context.rb:380:in `evaluate'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:489:in `block (2 levels) in eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:623:in `signal_status'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:486:in `block in eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:246:in `block (2 levels) in each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `loop'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:232:in `block in each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `catch'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb/ruby-lex.rb:231:in `each_top_level_statement'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:485:in `eval_input'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:395:in `block in start'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:394:in `catch'
/Users/username/.rbenv/versions/2.3.1/lib/ruby/2.3.0/irb.rb:394:in `start'
/Users/username/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'

Vérification des erreurs différentes

Si vous voulez faire des choses différentes en fonction du type d'erreur, utilisez plusieurs clauses de rescue , chacune avec un type d'erreur différent en tant qu'argument.

def divide(x, y)
  begin
    return x/y
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
    return nil
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  end
end

> divide(10, 0)
Don't divide by zero!

> divide(10, 'a')
Division only works on numbers!

Si vous souhaitez enregistrer l'erreur à utiliser dans le bloc de rescue :

rescue ZeroDivisionError => e

Utilisez une clause de rescue sans argument pour intercepter les erreurs d'un type non spécifié dans une autre clause de rescue .

def divide(x, y)
  begin
    return x/y
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
    return nil
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  rescue => e
    puts "Don't do that (%s)" % [e.class]
    return nil
  end
end

> divide(nil, 2)
Don't do that (NoMethodError)

Dans ce cas, essayer de diviser nil par 2 n'est pas un ZeroDivisionError ou un TypeError , donc il est géré par la clause de rescue par défaut, qui imprime un message pour nous informer qu'il s'agit d'une NoMethodError .

Réessayer

Dans une clause de rescue , vous pouvez retry pour exécuter la clause begin , probablement après avoir modifié les circonstances à l'origine de l'erreur.

def divide(x, y)
  begin
    puts "About to divide..."
    return x/y
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
    y = 1
    retry
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  rescue => e
    puts "Don't do that (%s)" % [e.class]
    return nil
  end
end

Si nous transmettons des paramètres dont nous savons qu’ils provoquent une TypeError , la clause begin est exécutée (marquée ici en imprimant "About to divide") et l’erreur est interceptée comme précédemment, et nil est renvoyé:

> divide(10, 'a')
About to divide...
Division only works on numbers!
 => nil

Mais si nous transmettons des paramètres qui provoquent une ZeroDivisionError , la clause begin est exécutée, l'erreur est interceptée, le diviseur passe de 0 à 1, puis retry l'exécution du bloc de begin (à partir du haut), maintenant avec un différent y . La deuxième fois, il n'y a pas d'erreur et la fonction renvoie une valeur.

> divide(10, 0)
About to divide...     # First time, 10 ÷ 0
Don't divide by zero!
About to divide...     # Second time 10 ÷ 1
=> 10

Vérification de l'absence de toute erreur

Vous pouvez utiliser une clause else pour le code qui sera exécuté si aucune erreur n'est générée.

def divide(x, y)
  begin
    z = x/y
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  rescue => e
    puts "Don't do that (%s)" % [e.class]
    return nil
  else
    puts "This code will run if there is no error."
    return z
  end
end

La clause else ne s'exécute pas si une erreur transfère le contrôle à l'une des clauses de rescue :

> divide(10,0)
Don't divide by zero!
=> nil

Mais si aucune erreur n'est déclenchée, la clause else s'exécute:

> divide(10,2)
This code will run if there is no error.
=> 5

Notez que la clause else ne sera pas exécutée si vous revenez de la clause begin

def divide(x, y)
  begin
    z = x/y
    return z                 # Will keep the else clause from running!
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
  else
    puts "This code will run if there is no error."
    return z
  end
end

> divide(10,2)
=> 5

Code qui doit toujours s'exécuter

Utilisez une clause ensure si vous souhaitez toujours exécuter du code.

def divide(x, y)
  begin
    z = x/y
    return z
  rescue ZeroDivisionError
    puts "Don't divide by zero!"
  rescue TypeError
    puts "Division only works on numbers!"
    return nil
  rescue => e
    puts "Don't do that (%s)" % [e.class]
    return nil
  ensure
    puts "This code ALWAYS runs."
  end
end

La clause ensure sera exécutée en cas d'erreur:

> divide(10, 0)
Don't divide by zero!   # rescue clause
This code ALWAYS runs.   # ensure clause
=> nil

Et quand il n'y a pas d'erreur:

> divide(10, 2)
This code ALWAYS runs.   # ensure clause
=> 5

La clause assure est utile lorsque vous voulez vous assurer, par exemple, que les fichiers sont fermés.

Notez que, contrairement à la clause else , la clause ensure est exécutée avant que la clause begin ou rescue renvoie une valeur. Si la clause ensure a un return qui remplace la valeur de return de toute autre clause!



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow