Defer in Ruby

Here’s the Ruby translation of the Go “Defer” example, formatted in Markdown for Hugo:

Our example demonstrates the use of ensure in Ruby, which is similar to defer in other languages. It’s used to ensure that a piece of code is executed, usually for cleanup purposes, regardless of whether an exception is raised or not.

require 'fileutils'

def main
  # Immediately after getting a file object with
  # create_file, we use a begin/ensure block to ensure
  # the file is closed after we're done writing to it.
  begin
    f = create_file("/tmp/ensure.txt")
    write_file(f)
  ensure
    close_file(f)
  end
end

def create_file(path)
  puts "creating"
  File.new(path, "w")
rescue StandardError => e
  puts "Error: #{e.message}"
  exit(1)
end

def write_file(file)
  puts "writing"
  file.puts "data"
end

# It's important to check for errors when closing a
# file, even in an ensure block.
def close_file(file)
  puts "closing"
  file.close
rescue StandardError => e
  warn "Error: #{e.message}"
  exit(1)
end

main

Running the program confirms that the file is closed after being written.

$ ruby ensure.rb
creating
writing
closing

In this Ruby version:

  1. We use a begin/ensure block in the main method to ensure that close_file is called regardless of what happens in the begin block.

  2. Instead of panic, we use Ruby’s exception handling with rescue to catch and handle errors.

  3. The createFile function is renamed to create_file to follow Ruby naming conventions.

  4. We use File.new instead of os.Create to create a new file.

  5. In close_file, we use warn to print error messages to STDERR.

  6. We don’t need to explicitly return the file object in create_file as Ruby automatically returns the last evaluated expression.

This example demonstrates how Ruby’s ensure can be used to handle cleanup operations, similar to defer in other languages.