RubyJuniorTechnical

В чём разница между nil, false и другими ложными значениями (falsy values) в Ruby?

В Ruby falsy только nil и false; 0, "", [] — truthy. nil означает отсутствие значения (NilClass), false — явный булев отказ (FalseClass); nil == false возвращает false.

Falsy-значения в Ruby: только nil и false

В Ruby ложными (falsy) являются ровно два значения: nil и false. Всё остальное — истинно (truthy), включая 0, пустую строку "", пустой массив [] и пустой хэш {}. Это принципиально отличает Ruby от JavaScript, Python и многих других языков.

nil vs false: в чём разница

nil — это объект класса NilClass, означающий отсутствие значения. false — объект класса FalseClass, явный булев отрицательный результат. В условном выражении оба ведут себя одинаково, но семантически различны.

puts nil.class    # NilClass
puts false.class  # FalseClass

# Оба falsy:
puts "nil is falsy"   if !nil    # выводит
puts "false is falsy" if !false  # выводит

# 0, "", [] — truthy в Ruby!
puts "0 is truthy"  if 0    # выводит
puts "[] is truthy" if []   # выводит
puts "'' is truthy" if ""   # выводит

Проверка на nil и на false

Для явного различения используйте методы nil? и операторы ==.

value = nil

value.nil?       # true  — только для nil
value == false   # falsenil не равен false
value == nil     # true

# Безопасный оператор &. — работает только с nil, не с false:
user = nil
user&.name       # nil, без NoMethodError

result = false
result&.to_s     # false — оператор &. пропускает false!
# false не nil, поэтому &. вызовет метод и получим NoMethodError
# для false.to_s это не проблема, но для несуществующего метода будет ошибка

Оператор || и его тонкости

Оператор || и метод ||= реагируют на оба falsy-значения. Это частая ловушка: если переменная может содержать false как валидное значение, ||= перезапишет его.

# Безопасно, если nil — единственное «нет значения»:
@cache ||= compute_value

# Опасно, если false — валидный результат:
enabled = false
enabled ||= true   # Теперь enabled = true! Это баг.

# Правильно:
enabled = false
enabled = compute_enabled if enabled.nil?

Метод fetch и значения по умолчанию

hash = { debug: false, verbose: nil }

# hash[:debug] || true — вернёт true, хотя ключ существует!
hash.fetch(:debug, true)    # false — правильно
hash.fetch(:missing, true)  # true  — ключа нет

Подводные камни

  • 0, "", [] — truthy: в отличие от Python/JS, пустые коллекции и ноль не ложны. Условие if array.length всегда истинно — используйте array.empty?.
  • ||= затирает false: flag ||= default перезапишет false. Если false — валидное значение, используйте defined?(flag) ? flag : default или явную проверку nil?.
  • &. не защищает от false: safe navigation operator пропускает только nil. На false он попытается вызвать метод и получит NoMethodError, если метод не существует у FalseClass.
  • nil != false: nil == false возвращает false. Не путайте семантику «нет значения» (nil) и «явное отрицание» (false).
  • Hash#[] vs Hash#fetch: hash[:key] вернёт nil для отсутствующего ключа И для ключа со значением nil. fetch различает эти случаи.
  • Array#compact удаляет только nil: [nil, false, 0].compact вернёт [false, 0]false не удаляется.
  • respond_to? на nil: nil.respond_to?(:to_s)true, у NilClass есть методы. Не считайте nil «пустым объектом без методов».

Common mistakes

  • Сводить nil false falsy к названию метода без lifecycle и failure path.
  • Игнорировать модель runtime: объектная динамическая модель Ruby, где почти всё является объектом, а методы ищутся через ancestor chain.
  • Не отделять validation, authorization, transaction boundary и business logic.
  • Менять похожие API местами без учёта семантики ошибок и ownership.

What the interviewer is testing

  • Объясняет nil false falsy через конкретную точку lifecycle в Ruby.
  • Приводит корректный минимальный пример без вымышленных методов или callbacks.
  • Называет edge cases: пустые значения, ошибки, транзакции, безопасность или concurrency.

Sources

Related topics