8

In Ruby, why does an uninitialized instance variable return nil while an uninitialized class variable raises a NameError?

Compare:

@some_uninitialized_variable # => nil

and:

@@some_uninitialized_class_variable # => NameError
enocom
  • 1,496
  • 1
  • 15
  • 22
  • 3
    While you're at it, uninitialized local variables also return a name error. – Cary Swoveland Feb 21 '15 at 21:06
  • @Jon, I think econom knows that. He wants to know `why?' – Cary Swoveland Feb 21 '15 at 21:30
  • Flagged Jon's comment as not constructive. – sawa Feb 21 '15 at 21:35
  • @Jon, "I can't say I support that decision." is a little like saying, "I am not in favour of the decision that I be flogged". :-) – Cary Swoveland Feb 21 '15 at 21:51
  • 1
    @CarySwoveland: Actually no, they don't: `if false then foo = 42 end; foo # => nil`. – Jörg W Mittag Feb 22 '15 at 01:03
  • @JörgWMittag, I stand by my first comment, as `if false then foo = 42 end` initializes `foo` to `nil`. No? – Cary Swoveland Feb 22 '15 at 01:57
  • @CarySwoveland: The assignment is never evaluated. All it does is remove the ambiguity between local variables and a receiverless method call without arguments. – Jörg W Mittag Feb 22 '15 at 02:00
  • @JörgWMittag, I would like to gain a better understanding of what's going on here. For example: `defined?(foo) #=> nil; if false then foo = 42 end; defined?(foo) => "local-variable"`. I understood that `foo` is initialize to `nil` by `foo = 42 if false` when it is parsed. The two expressions are equivalent and `defined?(foo) => "local-variable"` in both cases. Does that not suggest that `foo` is initialized to `nil` when `if false then foo = 42 end` is parsed, even though `foo = 42` is not executed? – Cary Swoveland Feb 22 '15 at 19:58

2 Answers2

8

My take is the following:

  • uninitialized local variables return a name error because Ruby doesn't know if its intended to be a local variable or a non-existent method.

  • if uninitialized class variables returned nil when not defined, it could lead to nasty bugs when the variable was actually assigned the value nil by a distant ancestor. That is, I see this as protecting the coder.

  • having instance variables default to nil when uninitialized if an oft-used feature: @a = @a || [].

Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
  • 2) Could you provide some (pseudo-)code showing a uninitialized class variable a) return `nil` b) raise an error. 3) You mean often, right? I don't agree with you. You can use `a = a || []` (or `a ||= []`). So variable doesn't need to be set default to `nil` in order to use `||`/`||=`. – Darek Nędza Feb 21 '15 at 22:31
  • @DarekNędza, regarding 2), all I meant was that if unitialized class variables retuned `nil`, this could happen: `class Vikki; @@v = nil; end; class Herb < Vikki; end; class Me < Herb; def self.v_initted?; !!@@v; end; end; Me.v_initted? #=> false`. As to 3), we agree. I just meant that Ruby could not return `nil` from `puts a` when `a` is uninitialized, not knowing if `a` were intended to be a local variable or undefined method. I regret I cannot answer 1). – Cary Swoveland Feb 22 '15 at 00:44
  • 2) You are checking if `@@v` is initialized. I expect `v_initted?` to return false or true. I would be very surprised if `v_initted?` have raised an error. In my opinion, protecting one coder (that use class variables - `@@var`) and not protecting other coder (that use instance variables - `@var`) is, at least, weird. 3) I am little confused. You mean something like this: ` x = x || puts(x)`? – Darek Nędza Feb 23 '15 at 15:44
  • if @@foo is not initialized, `@@foo = "bar"` won't work and gives an 'uninitialized class variable' error, but I'm wondering why `@@foo ||= "bar"` does works. I thought the `||=` operator checks for nil, but in this case seems like it also initialise @@foo – Paul Verschoor Dec 07 '21 at 10:17
  • @Paul, see [this explanation](https://www.oreilly.com/library/view/the-ruby-programming/9780596516178/ch04s02.html) of uninitialized variables. Specifically, search for the word, “quirk”. – Cary Swoveland Dec 07 '21 at 14:18
0

This seems to be the way Object#instance_variable_get works.

I am guessing here you are trying this in IRB. If you open up a prompt and enter self and self.class, you can see you are working on an Object called main.

But this does only answer the where part of the question (if there was any), not really the why part.

zwippie
  • 15,050
  • 3
  • 39
  • 54