2
class Something
    @@variable = 'Class variable'

    def give_me
        @@variable
    end
end

class OtherThing
    @variable = 'Instance variable with an interface'

    class << self
        attr_accessor :variable
    end

    def give_me
        self.class.variable
    end
end

p Something.new.give_me
p OtherThing.new.give_me

What I want to know is, which one should I use? Which are the benefits and cons of each?

Class variables are:

  1. Private unless you make an interface
  2. Shared between inheritances
  3. Shorter to write

Class-instance variables are:

  1. Public, since you must use an interface to access them
  2. Not shared between inheritances, but are set to nil when inherited
  3. Longer to write

What else should I have in mind?

Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
Zequez
  • 3,399
  • 2
  • 31
  • 42
  • "Should I use class variables or class-instance variables ... ?" - None of the above if it can be helped. There's usually never just one of anything. – Andrew Grimm Aug 16 '11 at 00:45
  • Class instance variables are private. Public instance variables don't exist in Ruby. If you want the accessor to be private, just define it as a private method. – Marnen Laibow-Koser Aug 16 '11 at 13:54
  • Andrew: Wrong. It's reasonably common for a class to need to store state that's not tied to any particular instance. – Marnen Laibow-Koser Aug 16 '11 at 13:55
  • @marnen: Can you give an example? – Andrew Grimm Aug 16 '11 at 22:39
  • 1
    Andrew: There are times when a class wants to know how many instances exist (OK, you could look in ObjectSpace, but what if you want to memoize it?) or where the class should store default constructor settings for newly created instances. I could go on; the point is that sometimes the class has its own properties like any other object. – Marnen Laibow-Koser Aug 18 '11 at 05:17

4 Answers4

3

I recently discovered ActiveSupport defines class_inheritable_accessor, which does what the class-instance variables do with the advantage that objects are not shared across inheritance, and you can have a default value for the variable when subclassing.

class Foo
  class_inheritable_accessor :x, :y
end

Foo.x = 1

class Bar < Foo
end

Bar.x #=> 1
Bar.x = 3
Bar.x #=> 3
Foo.x #=> 1

More info here

Just for completeness: of the two presented options, I prefer going with the class-instance variables, since is often the expected behavior.

Chubas
  • 17,823
  • 4
  • 48
  • 48
  • 1
    Wow, that's really weird! So Bar uses Foo's x, up until we try to set it, then poof! it differentiates? I'm having trouble seeing why the author of that whitepaper you linked to sounds so cheerful. :-) "Sometimes we may wish to have a third type of variable which combines the benefits of the above two, without their drawbacks: id est, a variable which transmits a default value across the inheritance tree, but that allows each subclass to privately modify it: welcome to Rails class_inheritable_accessor!" – AlexChaffee Sep 28 '11 at 01:24
3

Never use class variables in Ruby, ever. They cause problems with inheritance. Always use class instance variables instead.

Marnen Laibow-Koser
  • 5,959
  • 1
  • 28
  • 33
  • 1
    They don't cause problems with inheritance. Rather, they have potentially surprising semantics with respect to inheritance -- specifically, a class variable is shared among all subclasses of the class it's defined inside. That may or may not be what you want, but it's only a problem if you don't know about it. – AlexChaffee Sep 28 '11 at 01:13
  • I meant "problems" in the sense of "confusion". Indeed, they work, but in surprising ways. – Marnen Laibow-Koser Sep 29 '11 at 22:50
  • 4
    Oh, I know. I was just being pedantic. If you can't be pedantic on StackOverflow comments, then where can you? :-) – AlexChaffee Oct 02 '11 at 17:44
  • Upvoting for sheer snarkiness. – Marnen Laibow-Koser Oct 03 '11 at 21:14
1

You also have the option of declaring instance variables at the class level:

class Foo
    @bar = 'baz'

    def Foo.print
        puts @bar
    end
end

Overall, I'd avoid class variables, as the shared-across-inheritance model is often very surprising and non-obvious; to be honest, I'm not sure what utility they really offer, other than being a not-quite-global-global.

If you need a 'scoped' global variable, I'd go for class-level instance variables with accessors. You avoid the inheritance 'surprise', while still maintaining encapsulation.

Don Werve
  • 5,100
  • 2
  • 26
  • 32
1

Class instance variables are generally more useful and less surprising than class variables, so you should probably use them.

And now I shall spell some things out in the excruciating detail you've come to expect from a Ruby answer on StackOverflow:

To declare or refer to a class instance variable, you need to be in class scope and use a single @ sign. This places the variable on the singleton class instance for that class.

Unfortunately, when you're in class scope and use the def keyword, your methods are placed on the list of instance methods for that class, and are executed in instance scope, so their @-sign variables will be on the instance they're in.

What we want instead is to define methods on the class, not on its instances. What that really means is that these methods are on the list of instance methods for the singleton class of the class object. (Phew!)

So, to sum up: There are at least four idioms for switching over and defining methods on the singleton class of the class object Foo:

class Foo
  @a, @b, @c, @d = 1, 2, 3, 4

  # 1. pass the target to def
  def Foo.a
    @a
  end

  # 2. pass the target to def, relying on the fact that self 
  # happens to be the class object right now
  def self.b
    @b
  end

  # switch from class scope to singleton class scope
  class << self

    # 3. define a plain accessor in singleton class scope
    def c
      @c
    end

    # 4. use a macro to define an accessor
    attr_reader :d
  end

end

p [Foo.a, Foo.b, Foo.c, Foo.d]
#=> [1, 2, 3, 4]

(There are probably half a dozen more ways to do this, once you factor in class_eval and define_method and the like, but that should satisfy you for now. :-))

One final note: class instance variables are only available via the class they're defined on. If you try to call any of those methods from (or via) a subclass, the methods will execute, but the @ variables will all be nil, since self will be the subclass's class object, not the parent class' class object.

class Bar < Foo
end

p [Bar.a, Bar.b, Bar.c, Bar.d]
#=> [nil, nil, nil, nil]
AlexChaffee
  • 8,092
  • 2
  • 49
  • 55