Question
Ruby begin, rescue, and ensure Explained: Is ensure Like finally?
Question
In Ruby, is ensure the equivalent of finally in C#?
For example, should file handling be written like this?
file = File.open("myFile.txt", "w")
begin
file << "#{content}\n"
rescue
# handle the error here
ensure
file.close unless file.nil?
end
Or should it be written like this?
file = File.open("myFile.txt", "w")
begin
file << "#{content}\n"
file.close
rescue
# handle the error here
ensure
file.close unless file.nil?
end
Does ensure run no matter what, even when no exception is raised?
Short Answer
By the end of this page, you will understand how Ruby's begin, rescue, and ensure blocks work together, when ensure is executed, and how to use them safely for file handling and cleanup. You will also see the preferred Ruby style for opening and closing files.
Concept
Ruby uses begin, rescue, and ensure for exception handling and cleanup.
beginwraps code that might fail.rescuehandles exceptions.ensureruns cleanup code.
Yes: ensure is the Ruby concept most similar to finally in C#.
What ensure does
The ensure block is executed whether:
- an exception happens
- no exception happens
- the exception is rescued
- the method returns early
Its main purpose is cleanup:
- closing files
- releasing locks
- disconnecting from databases
- cleaning temporary resources
Why this matters
When you open a file, socket, or database connection, you should make sure it gets closed even if something goes wrong. Without cleanup, your program can leak resources or leave data in an inconsistent state.
Important detail
ensure does not mean "only run on error." It means "always run when leaving this block."
Mental Model
Think of begin, rescue, and ensure like visiting a workshop:
begin: you start the jobrescue: if something goes wrong, you handle the problemensure: before leaving, you always clean up your tools and lock the door
Even if the work goes perfectly, you still lock the door. Even if the work fails halfway through, you still lock the door.
That is exactly how ensure behaves: it is the "always clean up before exiting" step.
Syntax and Examples
Basic syntax
begin
# code that might raise an exception
rescue
# code that handles the exception
ensure
# code that always runs
end
Example: cleanup always runs
begin
puts "Writing data"
rescue
puts "Something went wrong"
ensure
puts "Cleanup happens here"
end
Output:
Writing data
Cleanup happens here
Even though no exception happened, ensure still ran.
Example: with an exception
begin
puts "Before error"
raise "Oops"
rescue
puts "Error handled"
ensure
puts "Cleanup happens here"
end
Output:
Step by Step Execution
Consider this code:
file = File.open("myFile.txt", "w")
begin
file << "Hello\n"
rescue => e
puts "Error: #{e.message}"
ensure
puts "Closing file"
file.close unless file.nil?
end
Step by step
File.open("myFile.txt", "w")opens the file for writing.- The
beginblock starts. file << "Hello\n"writes text to the file.- No exception happens, so
rescueis skipped. - The
ensureblock runs. puts "Closing file"prints a message.file.close unless file.nil?closes the file.
If writing fails
Imagine line 3 raises an exception.
- Ruby jumps out of the remaining code in
begin. - The
rescueblock runs and stores the exception in .
Real World Use Cases
Common uses for ensure
Closing files
file = File.open("log.txt", "a")
begin
file.puts("New log entry")
ensure
file.close unless file.nil?
end
Releasing database connections
A connection might need to be returned to a pool even if a query fails.
Unlocking resources
If your code acquires a mutex or lock, ensure is a safe place to release it.
Cleaning temporary state
You may create a temp file, change a config value, or switch directories temporarily. ensure helps restore the original state.
API and network code
Sockets, streams, and HTTP resources often need cleanup, especially when exceptions happen unexpectedly.
Real Codebase Usage
In real Ruby projects, developers often use ensure for cleanup that must always happen.
Common patterns
Guarding resource cleanup
file = nil
begin
file = File.open("data.txt", "w")
file.write("example")
rescue => e
warn "Write failed: #{e.message}"
ensure
file.close if file
end
This pattern is useful when File.open itself might fail. Initializing file = nil prevents a variable error in ensure.
Error logging plus cleanup
begin
perform_task
rescue => e
logger.error(e.message)
raise
ensure
cleanup_temp_files
end
Here, the error is logged, then re-raised, and cleanup still happens.
Early returns
def save_data
file = .open(, )
some_condition?
file.write()
file.close
Common Mistakes
1. Closing the file twice
Broken example:
file = File.open("myFile.txt", "w")
begin
file << "Hello\n"
file.close
ensure
file.close unless file.nil?
end
Why it is a problem:
- cleanup logic is duplicated
- it is harder to maintain
- double-closing is unnecessary
Better:
file = File.open("myFile.txt", "w")
begin
file << "Hello\n"
ensure
file.close unless file.nil?
end
2. Not handling failure when opening the file
Broken example:
file = File.open("missing/path/file.txt", "w")
begin
file.write("data")
ensure
file.close unless file.nil?
end
Why it is a problem:
Comparisons
ensure vs rescue
| Concept | Purpose | Runs on success? | Runs on error? |
|---|---|---|---|
rescue | Handle an exception | No | Yes |
ensure | Always perform cleanup | Yes | Yes |
ensure in Ruby vs finally in C#
| Ruby | C# | Purpose |
|---|---|---|
ensure |
Cheat Sheet
Quick reference
Basic structure
begin
risky_code
rescue => e
handle_error(e)
ensure
cleanup_code
end
Rules
begincontains code that may fail.rescueruns only if an exception is raised.ensureruns whether an exception happens or not.ensureis Ruby's closest equivalent to C#finally.
File handling
Manual style:
file = nil
begin
file = File.open("data.txt", "w")
file.write("hello")
ensure
file.close if file
end
Preferred Ruby style:
File.open("data.txt", "w") do |file|
file.write()
FAQ
Does ensure always run in Ruby?
Yes. ensure runs when leaving the begin block, whether an exception happened or not.
Is ensure the same as finally in C#?
They serve the same main purpose: guaranteed cleanup.
Should I close a file inside both begin and ensure?
No. Usually you should close it only once, typically in ensure, or better, use block-form File.open.
What is the most idiomatic way to open a file in Ruby?
Use block form:
File.open("myFile.txt", "w") do |file|
file.write("hello")
end
Ruby closes the file automatically.
Does rescue run when there is no exception?
No. rescue runs only when an exception is raised.
Mini Project
Description
Build a small Ruby script that writes log messages to a file while safely handling errors. This project demonstrates when ensure is useful and also shows the cleaner block-based alternative for file handling.
Goal
Create a script that writes text to a file, handles write errors, and guarantees the file is closed properly.
Requirements
- Open a file for writing.
- Write at least two lines to the file.
- Handle any exception that occurs during writing.
- Guarantee that the file is closed.
- Print a message showing whether the operation succeeded or failed.
Keep learning
Related questions
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.
Difference Between require and include in Ruby
Learn the difference between require and include in Ruby, when to load a file, and when to mix module methods into a class.
Fixing Ruby Gem Native Extension Errors: mkmf.rb Can't Find Header Files for Ruby
Learn why Ruby gem installs fail with missing ruby.h, how native extensions work, and how to fix header file errors on Linux servers.