skip to content

In Ruby, what classes are nil, true and false instances of, why is there no Boolean class, and what does !!value return?

level: juniorimportance: should knowfreq 50%

answer

  1. three objects, three classes
  2. NilClass, TrueClass, FalseClass under Object
  3. new is undefined on them
  4. ! is a method on BasicObject
  5. predicates may return truthy objects

basics

~20 s

nil, true and false are the only instances of NilClass, TrueClass and FalseClass, each inheriting directly from Object, so Ruby has no shared Boolean class. !!value returns true for any truthy value and false for nil or false.

solid answer

~40 s

`nil`, `true` and `false` are **singletons**: each is the only instance of `NilClass`, `TrueClass` or `FalseClass`. Their classes undefine `new`, so `NilClass.new` raises `NoMethodError`, and `dup` or `clone` returns the same object, so every `nil` in a program is identical. `TrueClass` and `FalseClass` both subclass `Object` directly; there is no common `Boolean` class, so a boolean check is `value == true || value == false`. `!` is a method on `BasicObject` that returns `true` only for `nil` or `false`; applying it twice turns any value into the canonical `true` or `false`, so `!!0` is `true` and `!!nil` is `false`. Use `!!` when a value leaves Ruby's truthiness world, such as a JSON flag, because predicates ending in `?` may return truthy objects: `Numeric#nonzero?` returns `self` or `nil`.

code

ruby · 12 lines
ruby
nil.class              # => NilClass
TrueClass.superclass   # => Object
FalseClass.superclass  # => Object
nil.dup.equal?(nil)    # => true

!!0                    # => true
!!""                   # => true
!!nil                  # => false
5.nonzero?             # => 5, truthy but not true
!!5.nonzero?           # => true

NilClass.new           # NoMethodError: undefined method 'new' for class NilClass

go deeper

for a junior

Recall the three classes, NilClass, TrueClass and FalseClass, and that !! turns any value into true or false according to truthiness.

for a middle

Explain how the singletons are enforced, no new and dup returning self, and why predicates like nonzero? may return a truthy object instead of true.

for a senior

Identify the boundaries where a real boolean is required, such as serialised flags and exact test assertions, and the trap of !! on blank strings.

for a principal

Weigh the absence of a Boolean type against explicit validation of external input, and set conventions for when public predicates must return true or false.

## Three singleton objects Ruby represents "no value", "yes" and "no" with three special objects. Each is a **singleton**: the one and only instance of its class. | Value | Class | Superclass | `to_s` | `inspect` | |---|---|---|---|---| | `nil` | `NilClass` | `Object` | `""` | `"nil"` | | `true` | `TrueClass` | `Object` | `"true"` | `"true"` | | `false` | `FalseClass` | `Object` | `"false"` | `"false"` | Ruby enforces the singleton property in several ways: - **No constructor.** The three classes undefine `new` and have no allocator, so `NilClass.new` raises `NoMethodError` with the message `undefined method 'new' for class NilClass`. - **No copies.** `nil.dup`, `nil.clone`, `true.dup` and the rest return the receiver itself. - **Identity everywhere.** Every occurrence of `nil` in a program is the same object, so `nil.equal?(nil)` is always `true` and `x.nil?` is the idiomatic test for it. Two more consequences follow from the objects being unique: - **Always frozen.** `nil.frozen?`, `true.frozen?` and `false.frozen?` all return `true`; in CRuby the three are immediate values encoded in the reference itself, so they need no allocation. - **The class is the singleton class.** `nil.singleton_class` returns `NilClass`, so `def nil.shout` defines an instance method on `NilClass` and changes `nil` everywhere in the process. ## Why there is no Boolean class `TrueClass` and `FalseClass` inherit straight from `Object`. They share no ancestor below `Object`, and core Ruby defines no `Boolean` class or module. Several reasons make this workable: - **Truthiness replaces a boolean type.** Conditions accept any object and test only for `nil` or `false`, so Ruby rarely needs to ask "is this a boolean?". - **Duck typing.** Ruby code usually asks what an object can do rather than what class it is. - **Each class is tiny.** `TrueClass` and `FalseClass` define little beyond `to_s`, `inspect`, `===` and the non-short-circuit operators `&`, `|` and `^`. When you do need to validate a boolean, for example a consent flag from a parsed request, check the two values directly: `value == true || value == false`. In plain Ruby, `value.is_a?(Boolean)` raises `NameError`, because the constant is not defined. ## The ! method and the !! idiom `!` is not only an operator; it is a method defined on `BasicObject`. Its default implementation returns `true` if the receiver is `nil` or `false`, and `false` otherwise. Applying it twice produces the canonical boolean for any value: 1. `!value` is `true` exactly when `value` is falsy. 2. `!!value` is therefore `true` exactly when `value` is truthy. 3. The result is always one of the two singletons, `true` or `false`, never the original object. So `!!0`, `!!""` and `!![]` are all `true`, and `!!nil` is `false`. Because `!` is a method, a class can redefine it, which changes what `!obj` returns but does not change how `if` treats the object: conditions test truthiness directly. ## When coercing to a real boolean matters Ruby's convention is that methods ending in `?` answer a yes-or-no question, but the syntax reference notes they may return any truthy object rather than exactly `true`: - `Numeric#nonzero?` returns `self` when the number is not zero and `nil` otherwise, so `5.nonzero?` is `5`. - `File.size?` returns the size as an `Integer`, or `nil` when the file is missing or empty. Inside Ruby this is harmless. It matters when the value crosses a boundary that distinguishes types: - **Serialised output.** A JSON field `has_middle_name` should hold `true` or `false`, not the middle name itself. - **Exact comparisons in tests.** An assertion that a predicate returns exactly `true` fails when it returns `5`. - **Public predicates.** A method you publish as `middle_name?` reads better when it returns a real boolean. ## A trap in the intake form `!!middle_name` answers "is `middle_name` truthy?", not "did the patient type one?". A blank form field arrives as `""`, which is truthy, so `!!middle_name` is `true`. For presence, write the question explicitly, for instance `!middle_name.to_s.empty?`, and use `!!` only when truthiness is really what you want to export.

  • In Ruby, how do true & value and true && value differ?
    `&` is a `TrueClass` method: its argument is evaluated before the call, and it returns `true` or `false`, so `true & "yes"` is `true`. `&&` is an operator that short-circuits and returns one of its operands, so `true && "yes"` is `"yes"`.
  • If a class redefines !, does an if statement see its objects as falsy?
    No. `if`, `while`, `&&` and `||` test truthiness directly and never call `!`, so only `nil` and `false` fail them. A redefined `!` changes only the result of an explicit `!obj` call, which is one reason redefining it is rarely a good idea.

saying these in an interview costs you the question

  • true and false are instances of a shared Boolean class
  • NilClass.new returns a fresh nil object
  • !!value checks that a string has characters
  • Methods ending in ? always return exactly true or false
  • !!0 returns false