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!