Skip to main content

Safe Navigation Operator(&.)

When you call a method on a nil object in Ruby, you get a NoMethodError. The safe navigation operator &. stops the chain early and returns nil instead of raising an error.

# raises NoMethodError if profile is nil
user.profile.avatar_url

# returns nil if profile is nil
user.profile&.avatar_url

How It Works

&. only skips calling the method if the receiver is nil. If the object exists, it behaves like the regular . operator.

user = nil
user&.name # => nil, no error

user = User.new
user&.name # => calls user.name normally

It's equivalent to writing:

user && user.profile && user.profile.avatar_url

API Response Example

A common case is handling optional nested fields from an external API where some keys may be absent or null:

# response.dig(:data, :user) might be nil if the user wasn't found

avatar = response.user&.profile&.avatar_url || "default.png"

The || "default.png" fallback kicks in when the chain returns nil, giving you a safe default.

&. only guards against nil. It does not guard against false.

obj = false
obj&.some_method # => NoMethodError: undefined method for false:FalseClass

If a value genuinely could be false, use an explicit nil? check or reconsider the data design so the field doesn't mix nil and false semantics.

&. vs Rails try

Rails provides a try method that looks similar:

user.try(:profile).try(:avatar_url)

The key difference: try silently returns nil if the method doesn't exist on the object. &. raises a NoMethodError in that case.

User.new&.nonexistent_method    # => NoMethodError
User.new.try(:nonexistent_method) # => nil

Prefer &. for standard nil-safe navigation. try is useful when you're genuinely unsure whether the method exists, which is less common with typed ActiveRecord models.

When to Use It

Good fit:

  • Optional associations (user.profile&.bio)
  • API responses with nullable fields
  • Optional query results (User.find_by(email: email)&.name)

Avoid it when:

  • You're chaining too deeply (3+ levels is a signal to refactor or add a guard clause)
  • You're working with values that could be false
  • The nil should never happen (let it raise so you catch the real bug)

References