ZetCode

Ruby begin Keyword

last modified October 18, 2026

The lowercase begin keyword opens a block that groups statements and is closed with end. In Ruby it is most often used for exception handling with rescue, else, and ensure, but it also forms begin...end while loops, can be used as an expression that yields a value, and works with retry to re-run a failing section. Note that this page covers the lowercase block only; for the uppercase BEGIN and END hooks, see Ruby BEGIN and END keywords.

begin as an Exception Handling Block

A begin block wraps code that may raise an exception. The rescue clause catches the error and lets the program continue. Without the block, the same handling would need to be written around a whole method body. The example below wraps a conversion in a begin block and rescues ArgumentError.

rescue_block.rb
# begin groups statements so rescue can catch errors from them
def read_number(text)
  begin
    value = Integer(text)
    puts "Parsed: #{value}"
    value * 2
  rescue ArgumentError => e
    puts "Error: #{e.message}"
    nil
  end
end

p read_number("21")
p read_number("abc")

The first call succeeds and returns the doubled value. The second call raises ArgumentError inside the begin block, so control jumps to the rescue clause, which returns nil.

$ ./rescue_block.rb
Parsed: 21
42
Error: invalid value for Integer(): "abc"
nil

begin with else and ensure

The else clause runs only when the begin body raises no exception. The ensure clause runs in every case, whether an exception occurred or not, which makes it suitable for cleanup. The example below shows both paths.

else_ensure.rb
# else runs only when no exception occurred; ensure always runs
def divide(a, b)
  begin
    result = a / b
  rescue ZeroDivisionError
    puts "Cannot divide by zero"
    result = nil
  else
    puts "Division succeeded"
  ensure
    puts "Cleanup finished for #{a} / #{b}"
  end
  result
end

p divide(10, 2)
p divide(1, 0)

For divide(10, 2) the else branch runs, followed by ensure. For divide(1, 0) the rescue branch runs instead of else, and ensure still runs afterward.

$ ./else_ensure.rb
Division succeeded
Cleanup finished for 10 / 2
5
Cannot divide by zero
Cleanup finished for 1 / 0
nil

begin...end while: Do-While Behavior

When while is attached to a begin...end block, the body runs once before the condition is checked. This differs from the while modifier on a plain statement, which checks the condition first and may run zero times. The same applies to until.

do_while.rb
# begin...end while runs the body at least once
i = 10
begin
  puts "begin..end while: i = #{i}"
  i += 1
end while i < 5

j = 10
while j < 5
  puts "while modifier: j = #{j}"
  j += 1
end
puts "while modifier ran 0 times"

k = 0
begin
  k += 1
end until k >= 3
puts "end until final k = #{k}"

The begin...end while loop executes its body once even though i < 5 is false from the start. The plain while loop checks j < 5 first, so its body never runs. The end until form runs the body until k reaches 3.

$ ./do_while.rb
begin..end while: i = 10
while modifier ran 0 times
end until final k = 3

begin as an Expression

Because begin...end is an expression, it can be assigned to a variable or passed as an argument. Its value is the value of the last expression evaluated inside it, or the value of a rescue clause if one is triggered.

expression.rb
# begin...end is an expression; its value is the last evaluated line
value = begin
  x = 6
  x * 7
end
puts "value = #{value}"

status = begin
  Integer("oops")
rescue ArgumentError
  "fallback"
end
puts "status = #{status}"

result = begin
  10 / 2
rescue ZeroDivisionError
  0
end
p result

The first begin block evaluates to 42, the last expression in it. In the second, Integer("oops") raises, so the value of the rescue clause, "fallback", is assigned instead. In the third, no exception occurs and the result of 10 / 2 is used.

$ ./expression.rb
value = 42
status = fallback
5

Retrying with begin and retry

The retry keyword, used inside a rescue clause, restarts the begin block from the top. It is a common pattern for transient failures such as network calls. A counter is needed to stop the retries, otherwise the loop would never end.

retry_block.rb
# retry re-runs the begin block after a rescued exception
attempts = 0
begin
  attempts += 1
  puts "Attempt #{attempts}"
  raise IOError, "connection failed" if attempts < 3
  puts "Connected after #{attempts} attempts"
rescue IOError => e
  puts "Rescued: #{e.message}"
  retry if attempts < 3
  puts "Giving up"
end

The first two attempts raise IOError. Each time the rescue clause calls retry, which jumps back to the start of the begin block. On the third attempt no error is raised, so the success message prints and the rescue clause is skipped.

$ ./retry_block.rb
Attempt 1
Rescued: connection failed
Attempt 2
Rescued: connection failed
Attempt 3
Connected after 3 attempts

Source References

Learn more from these resources: Ruby Exceptions Documentation, Ruby Control Expressions, and Ruby BEGIN and END keywords.

List all Ruby tutorials.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.