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. :)

Get the Honeybadger newsletter

Each month we share news, best practices, and stories from the DevOps & monitoring community—exclusively for developers like you.
    author photo
    Starr Horne

    Starr Horne is a Rubyist and Chief JavaScripter at Honeybadger.io. When she's not neck-deep in other people's bugs, she enjoys making furniture with traditional hand-tools, reading history and brewing beer in her garage in Seattle.

    More articles by Starr Horne
    An advertisement for Honeybadger that reads 'Turn your logs into events.'

    "Splunk-like querying without having to sell my kidneys? nice"

    That’s a direct quote from someone who just saw Honeybadger Insights. It’s a bit like Papertrail or DataDog—but with just the good parts and a reasonable price tag.

    Best of all, Insights logging is available on our free tier as part of a comprehensive monitoring suite including error tracking, uptime monitoring, status pages, and more.

    Start logging for FREE
    Simple 5-minute setup — No credit card required