Ruby Language
Uitzonderingen vangen met Begin / Rescue
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!