Closures in Ruby

Develop an understanding of what closures are, how and when to use them in ruby.
Asad Ahmed
Asad Ahmed
November 09, 2022

Ruby is an amazing programming language rich in features that encapsulate and make working with the language easier compared to other Object Oriented languages. At Harled Inc, we use Ruby and Ruby on Rails everyday to deliver high impact projects to our clients. We use closures to simplify, encapsulate and pass around data in our projects.

In this post, I will explain what closures are, the types of closures in Ruby and how to use them.

What is a Closure?

A closure is a section of functional code that has variables connected to the context in which it is invoked. A closure has a few characteristics, one of which is that it can be passed as an object, although it is not necessarily an object. Another important trait of a closure is that it 'remembers' the variables within its scope at the time of creation. When that closure is invoked, it preserves knowledge of its lexical context at the time it was defined since it can access variables that might not be in the current scope.

Block

Blocks are code snippets that can be created and executed later. In Ruby, blocks are a simple type of closure. As mentioned above, blocks, unlike almost everything else in Ruby, are not objects. A block can accept arguments and can return a value. A block in Ruby does not have a name of its own and is always called with a function or can be passed to a method call.

How to Define a Ruby Block

Blocks are enclosed in a do ... end statement or between brackets {}.

# do ... end block
for a in 1..5 do
puts i
end
# { ... } block
def greeting
puts "Hi, my name is Ruby."
end
greeting { puts "This is a block" }
view raw snippet_1.rb hosted with ❤ by GitHub

The output of the method will be 'Hi From the method'. This is because everything inside of the curly braces is a block that was never referenced within the method. Now, let's tell the method to use the block.

def greeting
puts "Calling from the inside of the method"
yield
puts "I am ending the method"
end
greeting { "The block is called" }
view raw snippet_2.rb hosted with ❤ by GitHub

Can you guess what the output would be when the method is called?

Calling from the inside of the method
The block is called
I am ending the method
view raw guess.rb hosted with ❤ by GitHub

Here we used the 'yield' keyword to explicitly call the block that we passed to the method call.

The method is executed from top to bottom but when execution flow hits the yield keyword, the flow is 'paused' so that the block can be executed. After the block is done, then the regular flow of execution continues.

How Do We Pass Parameters to a Block?

def block_with_params
yield 1
yield 2
end
block_with_params { |a| puts "a is #{a}" }
# --> a is 1
# --> a is 2
view raw snippet_3.rb hosted with ❤ by GitHub

Here, when the method is invoked, yield will call the block passed to the method call. The first time we pass the number 1 to the block as a parameter and it prints out the number 1. The second time we call the block, pass number 2 as a parameter and it prints out number 2.

Yield with a Return Value

We can return a value from a function that is using the yield keyword or even return the value of the block from within the method.

def return_yield_value
value = yield
puts value
end
return_yield_value { "return value from block" }
# --> return value from block
view raw snippet_4.rb hosted with ❤ by GitHub

Proc

A "proc" is an instance of the Proc class that contains a code block to be executed in a variable. You call Proc.new and pass it a block to create a proc.

my_proc = Proc.new { |name| puts "Hello, #{name}" }
def greet(proc)
proc.call('Ruby')
end
greet(my_proc)
# --> Hello Ruby
view raw snippet_5.rb hosted with ❤ by GitHub

Lambda

With a few exceptions, lambdas are effectively procs. They are more like "ordinary" methods in two ways: they use "normal" returns and enforce the number of arguments given when they are called.

lamb_1 = -> (n) { puts "lambda with #{name}" }
lamb_1.call("foo")
# --> Lambda with foo
view raw snippet_6.rb hosted with ❤ by GitHub

Instead of the arrow (->), you can also use the keyword 'lambda' to define a lambda function in Ruby.

Main Difference Between Procs and Lambdas

Procs and lambdas are virtually interchangeable, but they differ in a few ways:

  • While lambdas will raise an exception if there are too few parameters, procs don't care about the number of parameters.
  • Lambdas return from the lambda itself, whereas procs return from the current method.

Conclusion

Closures can be powerful when used correctly. You can write clean and readable code using these Ruby components. You can use closures to simulate Ruby classes. In addition to that, Ruby closures can be used to implement callbacks. Out of these, Blocks are the ones that you will probably interact with more on a daily basis.

I hope you have found this useful and that you have learned a thing or two about closures in Ruby. If you have any questions or feedback about this article feel free to contact me at asad@harled.ca or through LinkedIn.

Want to come and explore closures and other great ruby features with us? If so, we're actively hiring and would love for you to review out open positions!

We hope you've found this post helpful! If you have any feedback please reach out on X and we would be happy to chat. 🙏

About the author

Asad Ahmed

Asad is a Full Stack Developer based out of Kitchener, ON.