Question

Ruby &: Symbol-to-Proc Shortcut Explained

Question

In Ruby, I know that writing:

some_objects.each(&:foo)

is commonly used as a shorter form of:

some_objects.each { |obj| obj.foo }

My understanding is that &:foo somehow creates a block like { |obj| obj.foo }, converts it to a Proc, and passes it to each.

Why does this work in Ruby? Is &:foo just a special language shortcut, or is there a deeper reason in the language for why it behaves this way?

Short Answer

By the end of this page, you will understand how Ruby's & operator works with blocks, how a Symbol like :foo becomes a callable object through to_proc, and why some_objects.map(&:name) is a real language feature rather than a random special case. You will also see when this shortcut is helpful and when a normal block is clearer.

Concept

In Ruby, the &:method_name pattern is based on two language features working together:

  1. & in a method call means: convert this object into a block.
  2. Symbol#to_proc lets a symbol such as :foo turn into a Proc.

So when Ruby sees this:

some_objects.each(&:foo)

it roughly interprets it like this:

some_objects.each(&(:foo.to_proc))

And :foo.to_proc produces a proc that calls foo on whatever object it receives.

Conceptually, it behaves like:

proc { |obj| obj.foo }

That is why it works with iterator methods such as each, map, select, and similar methods that accept blocks.

Why this matters

This feature matters because Ruby treats behavior as data. Blocks, procs, methods, and objects that can be converted into procs all fit into Ruby's flexible style.

The &:symbol form is useful when:

  • you want to call the same method on every item in a collection
  • the block would otherwise be very short
  • readability improves through less repetition

For example:

users.map(&:name)

is a concise way to say:

users.map { |user| user.name }

This is not an isolated exception added only for Symbol. It is part of Ruby's broader rule that &object in a method call tries to call to_proc on that object.

Mental Model

Think of & as a block adapter.

A method like each wants a block. But instead of giving it a normal block directly, you hand Ruby an object.

some_objects.each(&:foo)

Ruby says:

  • “I need a block here.”
  • “You gave me :foo with an &.”
  • “Let me ask :foo to turn itself into a proc by calling to_proc.”
  • “Now I can use that proc as the block.”

A symbol like :foo is therefore acting like a label for a method name. Ruby converts that label into a callable block that says:

  • “When I receive an object, call foo on it.”

So you can imagine &:foo as a little remote control button labeled foo. Each object in the collection gets the same button pressed on it.

Take Quiz

Syntax and Examples

Core syntax

collection.map(&:method_name)

This is shorthand for:

collection.map { |item| item.method_name }

Example 1: Extract names

users = [
  OpenStruct.new(name: "Ava"),
  OpenStruct.new(name: "Ben"),
  OpenStruct.new(name: "Cara")
]

names = users.map(&:name)
puts names.inspect

Output:

["Ava", "Ben", "Cara"]

Explanation:

  • map loops through each user
  • &:name becomes a proc
  • that proc calls name on each user
  • map collects the returned names into a new array

Example 2: Convert strings to integers

numbers = ["1", "2", "3"]
ints = numbers.map(&:to_i)
puts ints.inspect

Output:

[1, 2, 3]

Example 3: Same result with a normal block

numbers = ["1", "2", "3"]
ints = numbers.map { |n| n.to_i }
puts ints.inspect

This is often easier for beginners to read. The shortcut is nice, but the block form is more explicit.

What & does generally

& is not only for symbols. It can also pass an existing proc as a block:

double = proc { |x| x * 2 }
result = [1, 2, 3].map(&double)
puts result.inspect

Output:

[2, 4, 6]

In both cases, & is about supplying a block.

Step by Step Execution

Consider this code:

words = ["cat", "dog", "bird"]
lengths = words.map(&:length)
puts lengths.inspect

Here is what happens step by step:

  1. words is an array containing three strings.
  2. map starts iterating through each element.
  3. Ruby sees &:length and knows map expects a block.
  4. Ruby calls to_proc on :length.
  5. That creates a proc that behaves roughly like:
proc { |item| item.length }
  1. map calls that proc for each item:
    • for "cat" → "cat".length → 3
    • for "dog" → "dog".length → 3
    • for "bird" → "bird".length → 4
  2. map collects the results into a new array.
  3. lengths becomes:
[3, 3, 4]

Important detail

This is only “roughly” equivalent to proc { |item| item.length }. The real implementation of Symbol#to_proc has Ruby-specific behavior, but this mental model is correct for normal use.

Real World Use Cases

Common places you will see &:symbol

Transforming arrays

products.map(&:price)
orders.map(&:id)
messages.map(&:strip)

Used when you want one property or one method result from every object.

Cleaning input data

params[:tags].map(&:strip).reject(&:empty?)

Useful for removing extra spaces and blank values.

Working with API or database results

users.map(&:email)
records.map(&:attributes)

Developers often extract fields from collections of model objects.

Type conversion

csv_row.map(&:to_s)
values.map(&:to_i)

Helpful when normalizing incoming data.

Boolean-style filtering

items.select(&:valid?)

This keeps only items for which valid? returns true.

When it fits best

Use &:symbol when the block only calls a single method on each element. If the logic needs conditions, multiple method calls, or extra variables, a normal block is usually clearer.

Take Quiz

Real Codebase Usage

In real Ruby projects, &:symbol appears most often in collection pipelines.

Common patterns

Simple mapping

user_names = users.map(&:name)

Validation and filtering

valid_orders = orders.select(&:valid?)

Cleanup chains

tags = raw_tags.map(&:strip).reject(&:empty?).uniq

Guarding readability

Developers often avoid &:symbol if the expression becomes harder to understand.

# Clear
users.map(&:name)

# Often clearer as a block
users.map { |user| user.profile.display_name }

How it relates to other Ruby patterns

  • Guard clauses: not directly related, but both aim to keep code concise and readable.
  • Validation: select(&:valid?) is common when filtering objects by a predicate method.
  • Error handling: if some objects may not respond to the method, developers usually avoid &:symbol unless they are sure the method exists.
  • Method pipelines: map(&:strip).reject(&:empty?) is a common functional style in Ruby.

Team style matters

Many teams use &:symbol only when:

  • the method call is obvious
  • all objects in the collection support the method
  • the shortened syntax improves readability

If not, they prefer the full block form.

Common Mistakes

1. Thinking &:foo is magic syntax only for symbols

It is not a one-off exception. & means “convert this object to a proc for use as a block.” Symbols work because Symbol defines to_proc.

2. Using it when the method needs extra logic

Broken for readability:

users.map(&:name.downcase)

This does not mean “call name.downcase on each user.”

Write this instead:

users.map { |user| user.name.downcase }

3. Using it on objects that do not respond to the method

items = ["a", 1, nil]
items.map(&:upcase)

This will fail because 1 and nil do not have upcase.

Safer alternative:

items.filter_map { |item| item.upcase if item.respond_to?(:upcase) }

4. Assuming it always improves readability

This is valid:

users.map(&:name)

But sometimes the explicit version is clearer:

users.map { |user| user.name }

Especially for beginners or when the collection contents are not obvious.

5. Confusing & in method definitions and method calls

In a method call:

array.map(&:to_s)

& converts an object to a block.

In a method definition:

def run(&block)
  block.call
end

&block captures a block as a Proc.

Same symbol, different direction.

6. Forgetting that map returns a new array

names = users.map(&:name)

This does not change users. It creates a new array containing the results.

Comparisons

&:symbol vs explicit block

StyleExampleBest whenNotes
&:symbolusers.map(&:name)One simple method callVery concise
Explicit block`users.map {uu.name }`

map(&:foo) vs each(&:foo)

MethodPurposeReturn value
map(&:foo)Transform each element into a new valueNew array of results
each(&:foo)Perform an action for each elementUsually returns the original collection

&proc vs &:symbol

FormExampleMeaning
&proc_objectarray.map(&double)Pass an existing proc as the block
&:symbolarray.map(&:to_i)Convert symbol to proc, then use as the block

When not to use &:symbol

SituationBetter choice
Need multiple method callsExplicit block
Need conditionalsExplicit block
Need local variablesExplicit block
Simple one-method call&:symbol is often fine
Take Quiz

Cheat Sheet

Quick reference

Basic form

collection.map(&:method_name)

Equivalent to:

collection.map { |item| item.method_name }

Why it works

  • & tells Ruby to treat something as a block
  • Ruby calls to_proc on the object after &
  • Symbol#to_proc turns :name into a proc that calls name

Common examples

users.map(&:name)
strings.map(&:upcase)
values.map(&:to_i)
orders.select(&:paid?)

Good use cases

  • one short method call per item
  • readable collection transformations
  • simple predicate filtering

Avoid when

  • you need chained logic like user.name.downcase
  • you need conditionals
  • not every object supports the method

Related & usage

Capture a block in a method definition:

def run(&block)
  block.call
end

Pass a proc as a block:

double = proc { |x| x * 2 }
[1, 2, 3].map(&double)

Rule of thumb

If the block is just “call one method on each item,” &:symbol is a good option. Otherwise, use a normal block.

FAQ

What does &:name mean in Ruby?

It means Ruby converts the symbol :name into a proc and uses it as the block. In practice, it calls name on each item.

Is &:symbol exactly the same as { |x| x.foo }?

For common usage, yes, that is the right mental model. Internally, Ruby uses Symbol#to_proc to create the proc.

Why does the ampersand work here?

In a method call, & tells Ruby to convert an object into a block by calling to_proc on it.

Is this only for symbols?

No. Any object that implements to_proc can be used with & in a method call.

When should I avoid &:symbol?

Avoid it when the logic is more than one simple method call, or when the shorter syntax makes the code harder to read.

Does each(&:foo) return the results of foo?

Usually no. each typically returns the original collection. If you want collected results, use map.

Can I write users.map(&:name.downcase)?

No. That is not valid for chaining method calls in the shortcut form. Use a normal block instead.

Is &block in a method definition the same thing?

It is related but used differently. In a method definition, &block captures a passed block as a Proc. In a method call, &object converts an object into a block.

Take Quiz

Mini Project

Description

Build a small Ruby script that processes a list of users and demonstrates when &:symbol is useful. This project shows how to extract values, filter records, and compare the shortcut with explicit blocks in a realistic collection-processing task.

Goal

Create a script that reads an array of user objects, prints all names, keeps only active users, and formats email addresses in lowercase.

Requirements

  • Define a simple User class with name, email, and active attributes.
  • Create an array with at least four user objects.
  • Use map(&:name) to collect user names.
  • Use a predicate method with select to keep only active users.
  • Use an explicit block for a transformation that needs more than one method call.
Take Quiz

Keep learning

Related questions

View more