Sök…


Ett grundläggande felhanteringsblock

Låt oss göra en funktion för att dela upp två siffror, det är mycket tillförlitligt med dess input:

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

Detta fungerar bra för många input:

> puts divide(10, 2)
5

Men inte allt

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

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

Vi kan skriva om funktionen genom att lägga in den riskfyllda delningsoperationen i en begin... end slutblock för att kontrollera om det finns fel och använda en rescue för att mata ut ett meddelande och returnera nil om det finns ett 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

Spara felet

Du kan spara felet om du vill använda det i 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>'

Kontrollera för olika fel

Om du vill göra olika saker baserat på typen av fel använder du flera rescue , var och en med en annan feltyp som ett 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!

Om du vill spara felet för användning i rescue :

rescue ZeroDivisionError => e

Använd en rescue utan argument för att fånga fel av en typ som inte anges i en annan 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)

I detta fall är det inte en ZeroDivisionError eller en TypeError försöka dela nil med 2, så det hanteras av standard rescue , som skriver ut ett meddelande för att låta oss veta att det var en NoMethodError .

Försöker igen

I en rescue klausul kan du använda retry att köra begin klausulen igen, förmodligen efter att ha ändrat den omständigheten som orsakade felet.

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

Om vi passerar parametrar som vi vet kommer att orsaka en TypeError , den begin klausul utförs (flaggas här genom att skriva ut "Om att dela") och felet fångas som tidigare, och nil returneras:

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

Men om vi passerar parametrar som kommer att orsaka en ZeroDivisionError , den begin klausul exekveras felet fångas, divisorn ändras 0-1, och sedan retry orsakar begin blocket ska köras igen (uppifrån), nu med en olika y . För andra gången finns det inget fel och funktionen returnerar ett värde.

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

Kontrollera om inget fel uppstod

Du kan använda en else klausul för kod som körs om inget fel tas upp.

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

Den else klausulen körs inte om det finns ett fel som överför kontrollen till en av rescue :

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

Men om inget fel tas upp körs den else klausulen:

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

Observera att else klausulen inte kommer att verkställas om du återvänder från begin klausul

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 som alltid ska köras

Använd en ensure klausul om det finns kod som du alltid vill köra.

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

Den ensure klausul kommer att utföras när det finns ett fel:

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

Och när det inte finns något fel:

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

Säkringsbestämmelsen är användbar när du till exempel vill se till att filer stängs.

Observera att, till skillnad från else klausul, den ensure är klausulen utförs innan begin eller rescue klausul returnerar ett värde. Om ensure klausul har en return som kommer att åsidosätta return för någon annan bestämmelse!



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow