xx77aBs xx77aBs - 7 months ago 18
Ruby Question

Passing block from one method to another

I need to pass a block from one method to another (I want to call

Rails.cache.fetch
with block passed to my method).

I can either add
&block
to parameter list and use that to pass it to the next method, or I can create a new block and call yield inside of it. I've wrote a short example and benchmark:

require "benchmark"

def with_block(&block)
do_something 'Test', &block
end

def with_yield
do_something('Test') { yield }
end

def do_something(string)
"#{yield} #{string}"
end

n = 5_000_000
Benchmark.bmbm do |x|
x.report("&block") do
n.times { with_block { "Yo" } }
end
x.report("yield") do
n.times { with_yield { "Yo" } }
end
end


&block 3.320000 0.010000 3.330000 ( 3.340438)
yield 1.670000 0.000000 1.670000 ( 1.669504)
--------------------------------- total: 5.000000sec

user system total real
&block 3.270000 0.010000 3.280000 ( 3.275914)
yield 1.680000 0.000000 1.680000 ( 1.682768)


Looks like
{ yield }
approach is much faster. Is it the right way to go? Are there any gotchas I'm not aware of because of calling
yield
inside a newly created block?

Answer

Short answer: Always use yield, unless you have a good reason to explicitly reference &block.

See: Why blocks make ruby methods 439% slower

With &block, you get a reified Proc on which you can do all kinds of stuff and which you can move around. However, with a yield and an implicit block, you are limited to only calling the block.

By using yield, the interpreter can bypass all the Proc reification as it knows the developer won't be able to use it; hence it can keep just a C-level structure instead of having to set up a Ruby-level object.