This blog will cover three common uses that will help clarify the purpose and application of the & operator.
- Convert a variable into a proc
- Shorten array enumerables
- Safe Navigation Operator
All of these things that you can do rely on the underlying magic that Ruby provides with the & operator
& just works, ok?
The first use we will explore is using & to allow us to pass a block to a method and do something useful with it.
def pretty_print(&block)
puts '**' << block.call.to_s << '**'
end
pretty_print {'hello world'}
That looks just like a regular method, only with braces, right? Not exactly. What would happen if you passed an enumerable? Suppose "('a'..'j').to_a" was our array of characters to be converted to uppercase before showing the entire array.
pretty_print { ('a'..'j').to_a.map do |letter| letter.upcase end }
You can't do this with a method parameter. The following doesn't work since it can't convert the Array to a String implicitly.
def test_print(t)
puts '**' << t << '**'
end
test(('a'..'j').to_a.map do |letter| letter.upcase end )
Shortening enumerables
What was happening in our example above was & represented the block that we passed to it. In fact & converts what you pass to it into either a block or proc, depending on how it is used. A common use is with #map and other enumerable methods that return a new array.
If you are using #map to create a new array from an existing object, you can likely cut down on some typing. Refactoring the example above would look like this.
pretty_print { ('a'..'j').to_a.map(&:upcase) }
Since we passed a block to &, Ruby converted this to a proc for us and the symbol :upcase refers to the method upcase which is available to the enumerable item. This could be useful with models that have a single attribute that you want an array of. For example, we could create an instance variable in one of our models.
@all_cats = Owners.all.map(&:cat)
Nice and simple.
Safe Navigation Marker
Another use for & is when you want to check a value, but just want nil back if it doesn't exist instead of the dreaded NoMethodError. This allows you to presume and not worry if something doesn't exist, you are prepared to get nil back.
@owner&.cat&.name
This is shorter an much easier to read than its counterpart
if @owner.cat
@owner.cat.name
else
nil
end
It is even better than the ternary version.
@owner.cat ? @owner.cat.name : nil
In this case, & is still converting what you pass to it as a block, but since the action occurs inside &'s block, if @owner doesn't have a cat and therefore the method for getting the cat's name doesn't exist, the block will evaluate to nil instead of throwing an error.
A powerful place to use this last example would be in a view page. You can lazily assume that parameters exist and just expect a nil output when that parameter or method doesn't exist.
And then?
There is also the bitwise use of & which wasn't covered in this blog. Stay tuned for more. & is an enigmatic symbol that produces interesting and useful ways to create flexible methods that wrap around other methods. Thank you for reading my blog and helping me understand these issues better with you.
Top comments (0)