Errors in Crystal

Our first example demonstrates error handling in Crystal. Crystal uses exceptions for error handling, which is different from the explicit return value approach used in some other languages.

# In Crystal, we use exceptions for error handling.
# We'll define a custom exception class for our specific error.
class CannotWorkWithFortyTwoError < Exception
end

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

# We can define custom error classes for specific scenarios
class OutOfTeaError < Exception
end

class PowerError < Exception
end

def make_tea(arg : Int32)
  case arg
  when 2
    raise OutOfTeaError.new("no more tea available")
  when 4
    # In Crystal, we don't need to wrap errors. We can just raise a new exception.
    raise PowerError.new("can't boil water")
  end
end

# Main execution
begin
  [7, 42].each do |i|
    begin
      result = f(i)
      puts "f worked: #{result}"
    rescue ex : CannotWorkWithFortyTwoError
      puts "f failed: #{ex.message}"
    end
  end

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

To run the program, save it as errors.cr and use the Crystal compiler:

$ crystal errors.cr
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 Crystal, we use exceptions for error handling. Instead of returning error values, we raise exceptions when an error occurs. We can define custom exception classes for specific error scenarios.

The begin/rescue blocks in Crystal are similar to try/catch blocks in other languages. They allow us to handle different types of exceptions separately.

Crystal doesn’t have a direct equivalent to Go’s error wrapping and errors.Is functionality. Instead, you can create custom exception hierarchies if you need to check for specific error types.

This example demonstrates basic error handling in Crystal, including raising and catching custom exceptions, which is the idiomatic way to handle errors in this language.