Errors in Ruby

In Ruby, it’s idiomatic to communicate errors via exceptions, which is different from the explicit error return values used in some other languages. Ruby’s approach makes it easy to handle errors using language constructs like begin, rescue, and ensure.

# In Ruby, we don't need to explicitly import modules for basic functionality

# We define a method that may raise an exception
def f(arg)
  if arg == 42
    # Instead of returning an error, we raise an exception
    raise ArgumentError, "can't work with 42"
  end
  
  # If no exception is raised, we simply return the result
  arg + 3
end

# We can define custom exception classes
class OutOfTeaError < StandardError; end
class PowerError < StandardError; end

def make_tea(arg)
  case arg
  when 2
    raise OutOfTeaError, "no more tea available"
  when 4
    # We can raise an exception with a nested cause
    raise PowerError, "making tea: can't boil water"
  end
  # If no exception is raised, the method implicitly returns nil
end

# Main execution
[7, 42].each do |i|
  # It's common to use a begin/rescue block for error handling
  begin
    result = f(i)
    puts "f worked: #{result}"
  rescue ArgumentError => e
    puts "f failed: #{e.message}"
  end
end

5.times do |i|
  begin
    make_tea(i)
    puts "Tea is ready!"
  rescue OutOfTeaError
    puts "We should buy new tea!"
  rescue PowerError
    puts "Now it is dark."
  rescue StandardError => e
    puts "unknown error: #{e.message}"
  end
end

When you run this Ruby script, you’ll get output similar to this:

f worked: 10
f failed: can't work with 42
Tea is ready!
Tea is ready!
We should buy new tea!
Tea is ready!
Now it is dark.

In Ruby, we use exceptions for error handling. The raise keyword is used to throw an exception, and rescue is used to catch and handle exceptions. This is similar to try/catch in many other languages.

Ruby doesn’t have a built-in way to wrap errors like Go’s fmt.Errorf with %w, but you can achieve similar functionality by setting the cause of an exception when you raise it, or by using custom exception classes that contain additional context.

The StandardError class is the default superclass for all standard errors. You can create custom error classes by inheriting from StandardError or any of its subclasses.

Ruby’s case statement is used here as an equivalent to Go’s if/else if chain in the makeTea function.

Remember that in Ruby, the last evaluated expression in a method is implicitly returned, so we don’t need explicit return statements in many cases.

This example demonstrates how Ruby’s exception handling mechanism can be used to achieve similar error handling patterns as in Go, while following Ruby’s idiomatic practices.