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)