When should I capture blocks?

I know by default that blocks aren’t strictly objects but can be turned into proc objects by capturing them. But I don’t understand under what circumstances capturing a block(s) and making it a proc is particularly useful.

Any use cases/examples?

It allows the block to persist after its method call.
For example, Hash has a constructor that takes a block and it will be called when a key is missing.

1 Like

Thank you!

Another example from the stdlib.
The block used to transform the values of the collection is not called at the moment of the method call and instead it’s saved for the time when the iterator is actually consumed.

Example:
(0..8).each.map { |n| n + 1 }.map { |n| n * 10 }.map { |n| n.to_s }.to_a

In the code above each block is stored as a proc of an Iterator::Map.
The range is not traversed until the final call to #to_a and when that happens all the procs are invoked on each element of the range in a single traversal.
Doing it with the usual #map (which just calls the block right then and there) would involve traversing the range + allocating an array on every step.

2 Likes

Another case is in recursive method that you want to pass a block. If the block is to be used in the recursive calls it needs to be captured.

Since calls with non-captured blocks produce an call site inline of the method for that block, the recursion will not work.