skip to content

In Ruby, what does the defined? keyword return for a local, an instance variable, a constant, a method and an undefined name?

level: middleimportance: should knowfreq 40%

answer

  1. a keyword, not a method
  2. a description string or nil
  3. "class variable" has a space
  4. parenthesise: low precedence
  5. does not run what it checks

basics

~20 s

defined? is a keyword that returns a string naming what its argument is, such as "local-variable", "instance-variable", "constant" or "method", or nil when the name is not defined, without running the method or assignment it inspects.

solid answer

~30 s

`defined?` is a **keyword**, not a method, and it returns a **description string or `nil`**: `"local-variable"`, `"instance-variable"`, `"global-variable"`, `"class variable"` (with a space), `"constant"`, `"method"`, `"assignment"`, `"yield"`, `"super"`, `"self"`, `"nil"`, `"true"`, `"false"` or `"expression"`. It does not call the method or perform the assignment it inspects, although for `defined?(obj.name)` it does evaluate `obj` to see whether it responds. It checks **existence, not truthiness**: `defined?(@rate)` is `"instance-variable"` even when `@rate` is `nil`. Its precedence is very low, so `defined? @a && @a.zero?` checks the whole `&&` and returns `"expression"`; always write `defined?(@a)`.

code

ruby · 14 lines
ruby
class Meter
  @@limit = 10

  def probe
    count = 1
    @rate = nil
    [defined?(count), defined?(@rate), defined?(@@limit),
     defined?(String), defined?(puts), defined?(@nope)]
  end
end

p Meter.new.probe
# => ["local-variable", "instance-variable", "class variable",
#     "constant", "method", nil]

go deeper

for a junior

Know that defined? returns a descriptive string or nil, and that it is safe to use on names that would otherwise raise.

for a middle

Explain the precedence trap and the existence-versus-truthiness distinction, and list several of the strings, including the space in class variable.

for a senior

Use defined? deliberately, for example to cache nil results, and know it evaluates a receiver, so side effects in the receiver still happen.

for a principal

Weigh defined? against reflection methods and respond_to? when setting conventions, favouring whichever makes intent clearest to reviewers.

## A keyword that describes instead of evaluating `defined?` looks like a predicate method, but it is a **keyword** handled by the compiler. That lets it inspect names that would raise if evaluated, such as an unknown local or an unset class variable. It returns a **string describing what the expression is**, or **`nil`** when the thing is not defined. | Expression | Result | |---|---| | `defined?(count)` after `count = 1` | `"local-variable"` | | `defined?(@rate)` after any assignment, even `@rate = nil` | `"instance-variable"` | | `defined?(@unset)` | `nil` | | `defined?(@@limit)` inside a class that assigned it | `"class variable"` (a space, not a hyphen) | | `defined?($stdout)` | `"global-variable"` | | `defined?(String)` | `"constant"` | | `defined?(puts)` | `"method"` | | `defined?(x = 1)` | `"assignment"` | | `defined?(yield)` | `"yield"` when a block was given, otherwise `nil` | | `defined?(super)` | `"super"` when a superclass method exists, otherwise `nil` | | `defined?(1 + 1)` | `"method"` (`+` is a method call) | | `defined?(@a && @b)` | `"expression"` | The complete set of strings is `nil`, `instance-variable`, `local-variable`, `global-variable`, `class variable`, `constant`, `method`, `yield`, `super`, `self`, `true`, `false`, `assignment` and `expression`. Code that compares against these strings must spell `"class variable"` with a space. ## What it evaluates and what it does not - It does **not** call the method it checks: `defined?(save_all!)` returns `"method"` without running `save_all!`. - It does **not** perform an assignment: `defined?(x = 1)` returns `"assignment"` and leaves `x` as `nil`. The parser still declares `x` as a local, because it saw an assignment. - For a call with an explicit receiver, such as `defined?(order.total)`, it **does** evaluate `order` to find out whether that object responds to `total`. A receiver with side effects will run. - For regexp match globals it reports on the current match: after a match without groups, `defined?($1)` is `nil`. ## The precedence trap `defined?` binds more loosely than `&&`, so the parentheses matter: 1. `defined? @a && @a.zero?` parses as `defined?(@a && @a.zero?)`. 2. An `&&` expression is described as `"expression"`, which is truthy. 3. The check therefore passes even when `@a` was never set. Write `defined?(@a) && @a.zero?` instead. That returns `nil` when `@a` is unset and `false` when it is set but not zero. ## Existence, not truthiness Because `defined?` asks whether a variable exists, it can tell an **unset** instance variable from one deliberately set to `nil` or `false`. That makes it useful for caching a lookup whose legitimate answer may be `nil`: ```ruby def discount_rate return @discount_rate if defined?(@discount_rate) @discount_rate = fetch_rate # may legitimately be nil end ``` After the first call, `@discount_rate` exists, so later calls return it without fetching again, even when it is `nil`. ## Patterns worth recognising - **Caching a nil result**: `return @x if defined?(@x)` returns the cached value even when it is `nil` or `false`. - **Optional super**: inside a module method, `super if defined?(super)` calls the next implementation only when one exists further up the ancestor chain, instead of raising `NoMethodError`. - **Guarding a constant**: `LIMIT = 10 unless defined?(LIMIT)` avoids a reassignment warning when a file may run twice. - **Feature detection**: `defined?(SomeLibrary)` checks whether a library's top-level constant has been loaded before using it. In each case the check is about whether something exists, not about its value, which is exactly what `defined?` answers. ## When to prefer something else - For guarding a constant definition, `defined?(NAME)` works, and `Module#const_defined?` is the reflective equivalent. - Ruby's own documentation notes that the specific reflection methods are less error-prone than `defined?` for instance variables and constants, because they avoid the precedence trap entirely. - Checking `respond_to?` is the usual way to ask whether an object supports a method at run time; `defined?(obj.name)` is rarer in application code. In an interview, the strong answer names the return type (a string or `nil`), gives three or four of the strings, and mentions the precedence trap and the existence-versus-truthiness distinction.

  • Why does defined? @a && @a.zero? return "expression" when @a was never set?
    `defined?` has lower precedence than `&&`, so without parentheses it receives the whole `@a && @a.zero?` expression. It does not evaluate it; it just reports that it is an expression, and the string `"expression"` is truthy. Writing `defined?(@a) && @a.zero?` checks the variable alone, returning `nil` when it is unset.
  • Does defined?(order.total) run any code?
    It does not call `total`, but it does evaluate the receiver `order` to check whether that object responds to `total`. If `order` is itself a method call with side effects, those side effects happen. It returns `"method"` when the receiver responds to the method and `nil` otherwise.

saying these in an interview costs you the question

  • defined? is a method on Object, so it can be overridden
  • defined? returns true or false like other ? methods
  • defined?(@rate) returns nil when @rate was set to nil
  • defined? runs the method it checks to see whether it raises
  • defined? @a && @b checks only @a without parentheses