Ricerca…


Un blocco di gestione degli errori di base

Facciamo una funzione per dividere due numeri, questo è molto fiducioso sul suo input:

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

Questo funzionerà bene per molti input:

> puts divide(10, 2)
5

Ma non tutto

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

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

Possiamo riscrivere la funzione avvolgendo l'operazione di divisione rischiosa in un begin... end blocco begin... end per verificare gli errori, e utilizzare una clausola di rescue per emettere un messaggio e restituire nil se c'è un problema.

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

Salvataggio dell'errore

È possibile salvare l'errore se si desidera utilizzarlo nella clausola di 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>'

Controllo di diversi errori

Se vuoi fare cose diverse in base al tipo di errore, usa più clausole di rescue , ognuna con un tipo di errore diverso come argomento.

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!

Se si desidera salvare l'errore per l'utilizzo nel blocco di rescue :

rescue ZeroDivisionError => e

Utilizzare una clausola di rescue senza argomento per rilevare errori di un tipo non specificato in un'altra clausola di 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)

In questo caso, provare a dividere nil per 2 non è un ZeroDivisionError o un TypeError , quindi viene gestito dalla clausola di rescue predefinita, che stampa un messaggio per farci sapere che si trattava di un NoMethodError .

Nuovo tentativo

In una clausola di rescue , è possibile utilizzare retry di eseguire nuovamente la clausola begin , presumibilmente dopo aver modificato la circostanza che ha causato l'errore.

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

Se passiamo i parametri che sappiamo causeranno un errore TypeError , viene eseguita la clausola begin (contrassegnata qui stampando "About to divide") e l'errore viene rilevato come prima e viene restituito nil :

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

Ma se passiamo i parametri che causano un ZeroDivisionError , la clausola begin viene eseguita, l'errore viene rilevato, il divisore modificato da 0 a 1, quindi retry fa begin blocco ZeroDivisionError (dall'alto), ora con un diverso y . La seconda volta non ci sono errori e la funzione restituisce un valore.

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

Verifica se non è stato sollevato alcun errore

È possibile utilizzare una clausola else per il codice che verrà eseguito se non viene generato alcun errore.

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 clausola else non viene eseguita se c'è un errore che trasferisce il controllo a una delle clausole di rescue :

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

Ma se non viene segnalato alcun errore, viene eseguita la clausola else :

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

Si noti che la clausola else non verrà eseguita se si torna dalla clausola 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

Codice che dovrebbe sempre funzionare

Utilizzare una clausola di ensure se esiste un codice che si desidera eseguire sempre.

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 clausola di ensure verrà eseguita quando si verifica un errore:

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

E quando non ci sono errori:

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

La clausola di verifica è utile quando si desidera assicurarsi, ad esempio, che i file siano chiusi.

Si noti che, a differenza della clausola else , la clausola di ensure viene eseguita prima che la clausola begin o rescue restituisca un valore. Se la clausola di ensure ha un return che sostituirà il valore di return di qualsiasi altra clausola!



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow