Szukaj…


Podstawowy blok obsługi błędów

Stwórzmy funkcję dzielącą dwie liczby, która jest bardzo ufna w odniesieniu do jej danych wejściowych:

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

Będzie to działać dobrze dla wielu danych wejściowych:

> puts divide(10, 2)
5

Ale nie wszystko

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

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

Możemy przepisać tę funkcję, zawijając ryzykowną operację podziału w bloku begin... end , aby sprawdzić błędy, i użyć klauzuli rescue , aby wyświetlić komunikat i zwrócić nil jeśli wystąpi problem.

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

Zapisywanie błędu

Możesz zapisać błąd, jeśli chcesz go użyć w klauzuli 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>'

Sprawdzanie różnych błędów

Jeśli chcesz robić różne rzeczy w zależności od rodzaju błędu, użyj wielu klauzul rescue , każda z innym typem błędu jako argumentem.

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!

Jeśli chcesz zapisać błąd do użycia w bloku rescue :

rescue ZeroDivisionError => e

Użyj klauzuli rescue bez argumentu, aby wyłapać błędy typu nieokreślonego w innej klauzuli 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)

W tym przypadku próba podzielenia nil przez 2 nie jest ZeroDivisionError ani TypeError , więc jest obsługiwana przez domyślną klauzulę rescue , która wypisuje komunikat informujący nas, że był to NoMethodError .

Ponowna próba

W rescue klauzuli, można użyć retry aby uruchomić begin ponownie klauzulę, przypuszczalnie po zmianie okoliczności, który spowodował błąd.

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

Jeśli mamy przekazać parametry, które znamy spowoduje TypeError The begin klauzula jest wykonywany (oznaczone tutaj drukując „O podzielenie”), a błąd zostanie złapany jak poprzednio, a nil jest zwracana:

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

Ale jeśli mamy przekazać parametry, które spowodują ZeroDivisionError The begin klauzula jest wykonywany, błąd zostanie złapany, dzielnik zmieniło od 0 do 1, a następnie retry powoduje, że begin blok należy uruchomić ponownie (od góry), teraz z inny y . Za drugim razem nie ma błędu, a funkcja zwraca wartość.

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

Sprawdzanie, czy nie wystąpił błąd

Możesz użyć klauzuli else dla kodu, który zostanie uruchomiony, jeśli nie zostanie zgłoszony błąd.

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

Klauzula else nie jest uruchamiana, jeśli występuje błąd, który przenosi kontrolę do jednej z klauzul rescue :

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

Ale jeśli nie zostanie zgłoszony błąd, klauzula else wykonuje:

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

Zauważ, że klauzula else nie zostanie wykonana, jeśli wrócisz z klauzuli 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

Kod, który powinien zawsze działać

Użyj klauzuli ensure , jeśli istnieje kod, który zawsze chcesz wykonać.

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

Klauzula ensure zostanie wykonana, gdy wystąpi błąd:

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

A kiedy nie ma błędu:

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

Klauzula upewnij się, gdy na przykład chcesz się upewnić, że pliki są zamknięte.

Należy zauważyć, że w przeciwieństwie do klauzuli else klauzula ensure jest wykonywana, zanim klauzula begin lub rescue zwróci wartość. Jeżeli ensure klauzuli ma return , który zastąpi return wartości każdej innej klauzuli!



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow