nil, true, false & Truthiness

Reviewed & published by Brayan K

Truthiness is the rule Ruby uses to decide whether a value counts as "yes" in a condition — and in Ruby the only falsy values are nil (nothing) and false; literally everything else is truthy.

Part of the free Ruby course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

That means 0 and "" are truthy — a fact that trips up newcomers from other languages. You'll also learn nil?, safe navigation &., and ||=.

What You'll Learn in This Lesson

1️⃣ The Only Two Falsy Values

Commit this to memory: in Ruby a condition is "false" only when the value is nil or false. Every other object — including 0, "", and [] — is truthy and will run an if block.

# In Ruby, ONLY nil and false are falsy. Everything else is truthy.
zero  = 0
blank = ""
empty = []
puts "0 is truthy"    if zero        # 0 is TRUTHY (unlike C/Python)
puts "'' is truthy"   if blank       # empty string is TRUTHY
puts "[] is truthy"   if empty       # empty array is TRUTHY
puts "nil is falsy"   unless nil     # nil is falsy
puts "false is falsy" unless false   # false is falsy

# nil and false are the ONLY two falsy values
[0, "", [], {}, nil, false].each do |v|
  puts "#{v.inspect} => #{v ? 'truthy' : 'falsy'}"
end

2️⃣ Checking for nil & Safe Navigation

To ask "is this nothing?", call .nil? — it's true only for nil, distinguishing it from false. When a value might be nil and you want to call a method anyway, the safe navigation operator &. returns nil instead of raising NoMethodError.

# nil? and explicit checks
name = nil
puts name.nil?            # true
puts name == nil          # true (works, but .nil? is idiomatic)

found = "result"
puts found.nil?           # false

# Beware: checking "if value" treats both nil AND false as missing.
flag = false
if flag.nil?
  puts "flag is truly missing"
else
  puts "flag is set (even though it's false)"
end
# Safe navigation &. — call a method only if the receiver isn't nil
user = nil
puts user&.upcase.inspect       # nil (no NoMethodError)

name = "ada"
puts name&.upcase               # ADA

# Without &., calling on nil blows up:
begin
  nil.upcase
rescue NoMethodError => e
  puts "Error: #{e.message}"
end

3️⃣ ||= Memoization & nil Conversions

x ||= y assigns only when x is currently nil or false — perfect for caching expensive results and for defaults. And nil converts politely: nil.to_s is "", nil.to_a is [], and nil.to_i is 0.

# ||= memoization: assign only if currently nil or false
@count = nil
@count ||= 0          # @count was nil, so it becomes 0
puts @count           # 0
@count ||= 99         # @count is 0 (truthy), so this is IGNORED
puts @count           # 0  (NOT 99)

# Common cache pattern
def expensive
  @cache ||= (puts "computing..."; 42)
end
puts expensive        # computes, prints "computing...", returns 42
puts expensive        # cached, no recompute

# nil converts gracefully
puts nil.to_s.inspect   # "" (empty string)
puts nil.to_a.inspect   # [] (empty array)
puts nil.to_i           # 0

Your turn. Fill in each ___ blank with the right operator, then run it.

# 🎯 YOUR TURN — fill in the blanks, then run with: ruby config.rb

config = nil
port = config___fetch(:port)     # 👉 safe navigation operator (two chars)
puts port.inspect                # nil, no crash

timeout = config&.fetch(:timeout) ___ 30   # 👉 fallback when left side is nil
puts "Timeout: #{timeout}"

value = ""
puts(value.empty? ? "blank but not nil" : "has content")

# ✅ Expected output:
#    nil
#    Timeout: 30
#    blank but not nil

Mini-Challenge: A Forgiving Greeting

Combine the falsy rules: treat an empty string as missing, then fall back to a default with ||. Run with ruby greet.rb.

# 🎯 MINI-CHALLENGE: a forgiving greeting.
# Save as greet.rb and run with: ruby greet.rb
#
# Write greeting(name) that:
#   1. Treats an empty string "" as missing (set it to nil).
#   2. Falls back to "stranger" using || when name is nil.
#   3. Returns "Hello, <who>!".
# Call it with "Ada", "", and nil.
#
# ✅ Example output:
#    Hello, Ada!
#    Hello, stranger!
#    Hello, stranger!

# your code here

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Truthiness

ConceptRuby Syntax
Falsy valuesnil , false
Is it nothing?value.nil?
Safe navigationuser&.name
Default / memoizex ||= y
Fallbackvalue || "default"
nil convertsnil.to_s / to_a / to_i

🎉 Lesson Complete!

Practice quiz

Which values are the ONLY falsy values in Ruby?

  • 0 and ""
  • nil and false
  • nil, false, and 0
  • nil and ""

Answer: nil and false. In Ruby only nil and false are falsy; everything else, including 0 and "", is truthy.

What does the expression: if 0 then "yes" end evaluate to?

  • nil
  • "yes"
  • false
  • an error

Answer: "yes". 0 is truthy in Ruby, so the if block runs and returns "yes".

What does nil.to_s return?

  • nil
  • "nil"
  • an empty string ""
  • a NoMethodError

Answer: an empty string "". nil converts gracefully: nil.to_s is "", nil.to_a is [], and nil.to_i is 0.

What does nil&.upcase return?

  • nil
  • ""
  • raises NoMethodError
  • "NIL"

Answer: nil. Safe navigation &. returns nil instead of raising when the receiver is nil.

Which method tells nil apart from false?

  • empty?
  • nil?
  • false?
  • blank?

Answer: nil?. value.nil? is true only for nil, distinguishing it from false.

After @count = nil; @count ||= 0; @count ||= 99, what is @count?

  • nil
  • 0
  • 99
  • false

Answer: 0. ||= assigns only when nil/false. @count becomes 0, then since 0 is truthy the second ||= is ignored.

What does "ada"&.upcase return?

  • nil
  • "ada"
  • "ADA"
  • an error

Answer: "ADA". The receiver isn't nil, so &. simply calls upcase normally, giving "ADA".

What does x ||= y expand to?

  • x = x && y
  • x || (x = y)
  • x = y
  • x && (x = y)

Answer: x || (x = y). x ||= y means x || (x = y): assign y only if x is currently nil or false.

Is an empty array [] truthy or falsy in Ruby?

  • falsy
  • truthy
  • nil
  • it raises an error

Answer: truthy. Empty collections are still objects, so [] is truthy. Use .empty? to test emptiness.

Calling a method on nil without a guard typically raises which error?

  • ArgumentError
  • NameError
  • NoMethodError
  • TypeError

Answer: NoMethodError. nil.upcase raises NoMethodError; use &. or a .nil? check to guard against it.

Continue this course

Frequently asked questions

Are 0 and an empty string really truthy in Ruby?

Yes. In Ruby the only two falsy values are nil and false — literally nothing else. The integer 0, the float 0.0, an empty string "", an empty array [], and an empty hash {} are all truthy, so if 0 and if "" both run their blocks. This differs sharply from C, Python, and JavaScript, where 0 and "" are falsy. When you want to know whether a collection is empty, ask explicitly with .empty? rather than relying on truthiness.

What's the difference between nil and false?

false is the boolean 'no'; it's the answer to a yes/no question. nil is Ruby's way of saying 'nothing here' — an absent or undefined value, like a hash key that doesn't exist or a method that returned nothing. They're different objects (NilClass vs FalseClass) but both are falsy, so a bare if value treats them the same. When the distinction matters — for instance, a flag that is deliberately false versus one that was never set — test with value.nil? instead of if value.

When should I use the safe navigation operator &. ?

Use &. when a value might be nil and you want to call a method on it without risking a NoMethodError. user&.name returns the user's name if user exists, or nil if user is nil, short-circuiting the rest of the chain. It's perfect for optional data like user&.address&.zip. Don't overuse it, though: if a value should never be nil, a plain user.name that loudly fails on nil is a better bug-catcher than silently returning nil.

What does ||= actually do?

x ||= y means 'assign y to x only if x is currently nil or false'. It expands to x || (x = y), so if x already holds a truthy value it is left untouched. This makes ||= the standard idiom for memoization (caching an expensive result in @cache ||= compute) and for default values. The one trap: because false counts as 'unset', ||= will overwrite a legitimately false value — use an explicit nil? check if false is a meaningful state.