3

Due to some sloppy coding on my part, I've noticed that undeclared instance variables seem to evaluate nil where undeclared local variables do not. Is this default nill value for instance variables intended behavior that I can exploit (like with a conditional to check whether the variable has been set true) or is this just a quirk that should be left alone?

2.6.6 :001 > puts @test

 => nil 
2.6.6 :002 > puts test
Traceback (most recent call last):
        2: from (irb):2
        1: from (irb):2:in `test'
ArgumentError (wrong number of arguments (given 0, expected 2..3))
neanderslob
  • 2,633
  • 6
  • 40
  • 82

2 Answers2

6

Per the Ruby doc:

Instance variables of ruby do not need declaration. This implies a flexible structure of objects. In fact, each instance variable is dynamically appended to an object when it is first referenced.

https://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/instancevars.html

You can also get a list of all previously defined instance variables:

ClassName.instance_variables

or

a = ClassName.new 
a.instance_variables #specific to instance
Sara Fuerst
  • 5,688
  • 8
  • 43
  • 86
  • 1
    Note that the two code blocks do not result in the same list. `ClassName.instance_variables` will return a list of instance variables of defined on the class while `ClassName.new.instance_variables` returns a list of instance variables of the instance of a class. As an example the class `class Foo; @a = 1; def initialize; @b = 2; end; end` then `Foo.instance_variables` returns `['@a']` and `Foo.new.instance_variables` returns `['@b']`. – 3limin4t0r Jan 13 '21 at 21:21
3

tl;dr Yes, this is documented in Assignment.

Whether a thing is defined,and what it is defined as, can be had with the defined? keyword.

2.6.5 :003 > defined?(fnord)
 => nil 
2.6.5 :004 > fnord = 42
 => 42 
2.6.5 :005 > defined?(fnord)
 => "local-variable" 
2.6.5 :006 > defined?($fnord)
 => nil 
2.6.5 :007 > $fnord = 42
 => 42 
2.6.5 :008 > defined?($fnord)
 => "global-variable" 
2.6.5 :009 > defined?(@fnord)
 => nil 
2.6.5 :010 > @fnord = 42
 => 42 
2.6.5 :011 > defined?(@fnord)
 => "instance-variable" 
2.6.5 :012 > defined?(FNORD)
 => nil 
2.6.5 :013 > FNORD = 42
 => 42 
2.6.5 :014 > defined?(FNORD)
 => "constant" 

This is useful for debugging, but I'd discourage "exploiting" it in application code.


First, let's clear up your example. test is a method of main. It is inherited from Kernel#test.

2.6.5 :004 > defined?(test)
 => "method" 
2.6.5 :005 > method(:test)
 => #<Method: main.test> 

A proper example looks like this.

2.6.5 :008 > puts @fnord

 => nil 
2.6.5 :009 > puts fnord
Traceback (most recent call last):
        4: from /Users/schwern/.rvm/rubies/ruby-2.6.5/bin/irb:23:in `<main>'
        3: from /Users/schwern/.rvm/rubies/ruby-2.6.5/bin/irb:23:in `load'
        2: from /Users/schwern/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
        1: from (irb):9
NameError (undefined local variable or method `fnord' for main:Object)

Local variables

The error hints at what's happening: ambiguity. fnord could be the local variable fnord or self.fnord. Neither have been declared, Ruby won't guess at what you meant, so you get a NameError.

In Ruby local variable names and method names are nearly identical. If you have not assigned to one of these ambiguous names ruby will assume you wish to call a method. Once you have assigned to the name ruby will assume you wish to reference a local variable.

Declared local variables can be discovered with local_variables.

2.6.5 :012 > foo = 42
 => 42 
2.6.5 :013 > Kernel.local_variables
 => [:foo, :_] 

Instance variables

An uninitialized instance variable has a value of nil. If you run Ruby with warnings enabled, you will get a warning when accessing an uninitialized instance variable.

There are various methods for introspecting instance variables like instance_variables and instance_variables_defined?. While there are some instances this might be necessary when writing libraries, I strongly discourage exploiting the distinction between nil and "undefined" in application code; it makes things fragile.

Class variables

Accessing an uninitialized class variable will raise a NameError exception.

Global variables

An uninitialized global variable has a value of nil.

Schwern
  • 153,029
  • 25
  • 195
  • 336