Samuel Williams Tuesday, 04 August 2009

The following code surprised me:

begin
  1 / 0
rescue
  puts "Rescue"
  exit
ensure
  puts "Ensure"
end

The result of this code is:

Rescue
Ensure

Like all things in ruby, there is a way to bypass this:

begin
  1 / 0
rescue
  puts "Rescue"
  exit!
ensure
  puts "Ensure"
end

The result of this code is:

Rescue

One of the interesting side-effects of processing ensure blocks is that we can actually cancel the exit function:

begin
  begin
    1 / 0
  rescue
    puts "Rescue"
    exit
  ensure
    puts "Ensure"
 
    1 / 0 # Raise another exception while processing exit
  end
rescue
  puts "Outer Rescue"
end
 
puts "More stuff"

The surprising result of this program is:

Rescue
Ensure
Outer Rescue
More stuff

Therefore, even though we asked ruby to exit, because of the second exception, the program kept running! The reason for this is that exit doesn't actually do what it says it does; actually it simply raises the SystemExit exception, which when received at the top level, causes the interpreter to stop.

begin
  exit
rescue SystemExit
  puts "Caught SystemExit"
end

The result of this code is:

Caught SystemExit

This is by design: It means that you can process exit in a way other than exiting a program. For example, in a networking library, you might not want individual connections exiting the entire app, so you can can trap this kind of behaviour and deal with it specifically.

Comments

Leave a comment

Please note, comments must be formatted using Markdown. Links can be enclosed in angle brackets, e.g. <www.codeotaku.com>.