Reputation: 4768
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?
Upvotes: 3
Views: 4247
Reputation: 28285
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.
Upvotes: 4