I'm learning Ruby and have been told that, to quote Codecademy:
When passing a proc to a method, an & is used to convert the proc into a block.
Hence the following works:
cube = Proc.new do |num| num ** 3 end
puts [1, 2, 3].map!(&cube) #errors without &
But if I pass a proc to a user-defined function, it works only without the &
and errors with it.
foo = Proc.new do puts 'foo' end
def bar(cb)
cb.call
end
bar(foo) #bar(&foo) results in error
So how should I understand the whole with-&
and without-&
difference? Is it to do with inbuilt methods (map()
, in my case) vs. user-defined methods?
CodePudding user response:
If I pass a proc to a user-defined function, it works only without the & and errors with it.
That's because in your example cb
is a positional argument. To declared it as a block argument, you need to prefix it with &
:
def bar(&cb)
cb.call
end
foo = Proc.new { puts 'foo' }
bar(&foo)
Or simply:
def bar
yield
end
foo = Proc.new { puts 'foo' }
bar(&foo)
Having an actual block argument also allows you to skip the proc creation and call it as:
bar { puts 'foo' }
# or
bar do
puts 'foo'
end
So how should I understand the whole with-
&
and without-&
difference?
There are 3 kinds of method arguments in Ruby:
- positional arguments, e.g.
foo(1, 2, 3)
- keyword arguments, e.g.
foo(a: 1, b: 2, c: 3)
- block arguments, e.g.
foo { puts "hello" }
Since procs are objects, you can pass them as positional or keyword arguments, just like any other object.
But if you want a proc to become a block argument, you need &
to convert it.