Ruby's class variables are confusing. Even expert Ruby users can find them hard to intuit. The most obvious example has to do with ineritance:
class Fruit
@@kind = nil
def self.kind
@@kind
end
end
class Apple < Fruit
@@kind = "apple"
end
Apple.kind
# => "apple"
Fruit.kind
# => "apple"
Changing the kind
variable on the child class also changes it on the parent. It's pretty messed up. But this is just how the language is supposed to work. It was a design decision made a long time ago to mimic Smalltalk.
It gets worse
There are other examples of class variable weirdness that don't seem to be architectural choices so much as implementation quirks. Today I'm going to talk a little bit about one of these that I find interesting.
We're going to compare two pieces of code now. They look like they should produce an identical result but they don't.
In our first example we set a class variable and create a method to return it. There is absolutely nothing fancy going on here. And everything works as expected.
class Foo
@@val = 1234
# This is shorthand for declaring a class method.
class << self
def val
@@val
end
end
end
Foo.val
# => 1234
Perhaps you didn't know this, but class << self
doesn't have to be inside the class definition. In the example below we've moved it outside. A class method is added but it can't access the class variable.
class Bar
@@val = 1234
end
class << Bar
def val
@@val
end
end
Bar.val
# warning: class variable access from toplevel
# NameError: uninitialized class variable @@val in Object
When we try to access the class variable from our function we get a warning and an exception. What's going on?
Enter lexical scope
I'm becoming more and more convinced that lexical scope is the source of 99% of the weird and confusing aspects of Ruby.
In case you're not familiar with the term, lexical scoping refers to grouping things together based on where they appear in the code, not on where they belong in an abstract object model. It's a lot easier just to show you an example:
class B
# x and y share the same lexical scope
x = 1
y = 1
end
class B
# z has a different lexical scope from x and y, even though it's in the same class.
z = 3
end
Class is determined Lexically
So how is lexical scope at work in our class variable example?
Well, in order to retrieve a class variable Ruby has to know which class to get it from. It uses lexical scoping to find the class.
If we look more closely at the working example we see that the code that accesses the class variable is physically with in the class definition.
class Foo
class << self
def val
# I'm lexically scoped to Foo!
@@val
end
end
end
In the nonworking example, the code that accesses the class variable is not lexically scoped to the class.
class << Bar
def val
# Foo is nowhere in sight.
@@val
end
end
But if it's not lexically scoped to the class, what is it scoped to? The warning that Ruby prints gives us a clue: warning: class variable access from toplevel
.
In the nonworking example it turns out that the class variable is lexically scoped to the top level object. This can cause some really strange behavior.
For example, if we tried to set a class variable from code that is lexically scoped to main, the class variable gets set on Object
.
class Bar
end
class << Bar
def val=(n)
# This code is lexically scoped to the top level object.
# That means, that `@@val` will be set on `Object`, not `Bar`
@@val = i
end
end
Bar.val = 100
# Whaa?
Object.class_variables
# => [:@@foo]
More examples
There are lots of ways to reference class variables outside of the lexical scope of the class. All of them will give you trouble.
Here are a few examples for your enjoyment:
class Foo
@@foo = :foo
end
# This won't work
Foo.class_eval { puts @@foo }
# neither will this
Foo.send :define_method, :x do
puts @@foo
end
# ..and you weren't thinking about using modules, were you?
module Printable
def foo
puts @@foo
end
end
class Foo
@@foo = :foo
include Printable
end
Foo.new.foo
And now you know why everyone says never to use class variables in Ruby. :)