В чём разница между 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 # false — nil не равен 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.