Zoeken…


Een standaard foutafhandelingsblok

Laten we een functie maken om twee getallen te delen, die veel vertrouwen heeft in de invoer:

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

Dit werkt prima voor veel invoer:

> puts divide(10, 2)
5

Maar niet alles

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

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

We kunnen de functie herschrijven door de risicovolle deling in een begin... end eindblok te verpakken om te controleren op fouten, en een rescue om een bericht uit te voeren en nil retourneren als er een probleem is.

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

De fout opslaan

U kunt de fout opslaan als u deze in de rescue wilt gebruiken

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>'

Controleren op verschillende fouten

Als u verschillende dingen wilt doen op basis van het soort fout, gebruikt u meerdere rescue , elk met een ander fouttype als 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!

Als u de fout wilt opslaan voor gebruik in het rescue :

rescue ZeroDivisionError => e

Gebruik een rescue zonder argument om fouten van een type te vangen dat niet in een andere rescue gespecificeerd.

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)

In dit geval is het proberen om nil door 2 te delen nil ZeroDivisionError of een TypeError , dus het werd afgehandeld door de standaard rescue , die een bericht afdrukt om ons te laten weten dat het een NoMethodError .

Nieuwe poging

In een rescue clausule, kunt u retry om te draaien het begin clausule weer, vermoedelijk na het veranderen van de omstandigheid dat de fout heeft veroorzaakt.

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

Als we passeren parameters waarvan we weten dat een veroorzaken TypeError , het begin clausule wordt uitgevoerd (hier gemarkeerd door het afdrukken van "About to divide") en de fout wordt betrapt als voorheen, en nil wordt geretourneerd:

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

Maar als we parameters dat zal leiden tot voorbij ZeroDivisionError het begin clausule wordt uitgevoerd, de fout wordt gevangen, de deler veranderde 0-1, en vervolgens retry zorgt de begin blok opnieuw worden uitgevoerd (van boven), nu met een verschillende y . De tweede keer is er geen fout en retourneert de functie een waarde.

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

Controleren of er geen fout is opgetreden

U kunt een else clausule gebruiken voor code die wordt uitgevoerd als er geen foutmelding wordt gegeven.

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

De else clausule wordt niet uitgevoerd als er een fout is waardoor de besturing wordt overgedragen aan een van de rescue :

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

Maar als er geen fout wordt gemaakt, wordt de else clausule uitgevoerd:

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

Merk op dat de clausule else niet wordt uitgevoerd als u terugkeert van de clausule 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 die altijd moet worden uitgevoerd

Gebruik een ensure clausule als er code die u altijd wilt uitvoeren.

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

De ensure wordt uitgevoerd als er een fout is:

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

En wanneer er geen fout is:

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

De clausule verzekeren is handig wanneer u er bijvoorbeeld voor wilt zorgen dat bestanden worden gesloten.

Merk op dat, in tegenstelling tot de else clausule, de ensure clausule wordt uitgevoerd vóór het begin of rescue clausule geeft een waarde. Als de ensure clausule heeft een return dat de voorrang op return waarde van een andere clausule!



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow