Ruby Symbol to Proc explained, the short version

What does the “ampersand, symbol” thingy as an argument to a ruby method actually do? It creates a proc.

What’s with this weird looking syntax with an ampersand and a symbol

irb(main):001:0> [ 1, 2, 3, 4, 5 ].select(&:even?)
=> [2, 4]
irb(main):002:0> [ 1, 2, 3, 4, 5 ].map(&:even?)
=> [false, true, false, true, false]
irb(main):001:0> a_proc = proc { |a, b, c| p a, b, c }
irb(main):002:0> a_proc.call(1)
1
nil
nil
=> [1, nil, nil]
irb(main):003:0> a_proc.call(1,2,3,4)
1
2
3
=> [1, 2, 3]
irb(main):004:0> a_proc.arity
=> 3
irb(main):005:0> a_proc.parameters
=> [[:opt, :a], [:opt, :b], [:opt, :c]]
irb(main):006:0> a_lambda = lambda { |a, b, c| puts a, b, c }
irb(main):007:0> a_lambda.arity
=> 3
irb(main):008:0> a_lambda.parameters
=> [[:req, :a], [:req, :b], [:req, :c]]
irb(main):017:0> :even?.to_proc.lambda?
=> false
irb(main):020:0> :even?.to_proc.arity
=> -1
irb(main):022:0> :even?.to_proc.parameters
=> [[:rest]]
irb(main):001:0> proc { |*a| p a }.arity
=> -1
irb(main):002:0> proc { |*a| p a }.parameters
=> [[:rest, :a]]
irb(main):001:0> proc { |*| }.parameters
=> [[:rest]]
irb(main):023:0> :even?.to_proc.call
Traceback (most recent call last):
4: from /Users/pierre/.rbenv/versions/2.7.1/bin/irb:23:in `<main>'
3: from /Users/pierre/.rbenv/versions/2.7.1/bin/irb:23:in `load'
2: from /Users/pierre/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/irb-1.2.3/exe/irb:11:in `<top (required)>'
1: from (irb):23
ArgumentError (no receiver given)
irb(main):024:0> :even?.to_proc.call(1, 2)
Traceback (most recent call last):
6: from /Users/pierre/.rbenv/versions/2.7.1/bin/irb:23:in `<main>'
5: from /Users/pierre/.rbenv/versions/2.7.1/bin/irb:23:in `load'
4: from /Users/pierre/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/irb-1.2.3/exe/irb:11:in `<top (required)>'
3: from (irb):23
2: from (irb):24:in `rescue in irb_binding'
1: from (irb):24:in `even?'
ArgumentError (wrong number of arguments (given 1, expected 0))

It’s not just for parameter-less methods

irb(main):004:0> [ 5, 3, 1, 2, 4 ].sort(&:<=>)
=> [1, 2, 3, 4, 5]
irb(main):004:0> [ 5, 3, 1, 2, 4 ].sort { |a, b| a.<=>(b) }
=> [1, 2, 3, 4, 5]

We can pass almost anything after the ampersand

irb(main):001:1* def a_method(&block)
irb(main):002:1* block.call(1)
irb(main):003:0> end
=> :a_method
irb(main):004:0> a_method(&{ 1 => 'one' })
=> "one"
irb(main):005:0> a_method(&{ 2 => 'two' })
=> nil
irb(main):004:1* class A
irb(main):005:2* def self.to_proc
irb(main):006:2* proc { puts "Not really useful but it works" }
irb(main):007:1* end
irb(main):008:0> end
=> :to_proc
irb(main):009:0> a_method(&A)
Not really useful but it works
=> nil

Links:

Conclusion

VALUE
rb_sym_to_proc(VALUE sym)
{
}

Appendix, Arguments vs Parameters

def a_method(a, b)
end
a_method('a', 20)

Written by

Software Engineer by day, Runner by day as well.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store