1

I offen see ruby project use instance variable @var in class scope in a ruby class.

I think in a class method, class variable @@var should be used, and in an instance method, instance variable @var be used.

I wanna if using @var in class scope is a right way?

Currently I known the difference between using instance variable and class variable in class scope is that instance variable can't be inherited by sub class. The code demonstrates the defference.

class Foo
  @bar = 8
  @@bar2 = 10
  def self.echo_bar
    p @bar
  end

  def self.echo_bar2
    p @@bar2
  end
end

class Foo2 < Foo

end

Foo.echo_bar
Foo.echo_bar2

Foo2.echo_bar
Foo2.echo_bar2
# Result:
# 8
# 10
# nil
# 10
4
  • 1
    It's may be right way, depending on context, of course. Commented May 6, 2014 at 10:08
  • In rails, I almost see @var used in class scope, maybe just because @@var looks ugly? github.com/rails/rails/blob/master/railties/lib/rails/… Commented May 6, 2014 at 10:12
  • 4
    It has absolutely nothing do with how it looks. The semantics of instance variables and class variables are totally different, you cannot interchange them. Commented May 6, 2014 at 10:17
  • 1
    In Rails you won't see class variables because class_attribute (a method defined by ActiveSupport) is preferred. class_attribute was created because class variables are shared between superclasses and subclasses, thus modifications on their values inside subclasses are applied on superclasses too, which rarely is desired. Using class_attribute this effect is prevented Commented May 6, 2014 at 10:33

2 Answers 2

3

A Ruby object is:

  1. Data - A map of instance variable and the values associated. Note that instance variables are private to the instance itself.
  2. Behavior - A pointer to a class where the methods this instance responds to are defined. Note methods may be inherited from parent class.

Class is an object in Ruby. By writing:

class Foo
  @bar = 8
  def self.get_bar
    @bar
  end
end

You get the following data model (not exactly, the hierarchy of Foo's ancestors and their eigen classes have been removed):

                             Class
                               |
Foo        class ptr    [eigenclass of Foo]
@bar = 8   --------->   method - get_bar 

The instance variable at class scope defines the private data for that class object. From the model above, this instance variable is accessible through instance methods defined in the eigenclass of Foo, and in its ancestor chain Class, Module, Object, Kernel, BasicObject. These instance variables defined some data associated with the class object Foo.

By writing:

class FooChild < Foo
end

You have

                             Class
                               |
Foo         class ptr    [eigenclass of Foo]
@bar = 8    --------->   method - get_bar
    |                          |
FooChild    class ptr    [eigenclass of FooChild]
<Nothing>   --------->   <Nothing>

Class object FooChild inherits the method get_bar from the eigen class of Foo, so you can call FooChild.get_bar. However, since the receiver is FooChild, and no instance variable @bar is associated with FooChild, the default value nil will be returned.

The above analysis is more detailed in the book Meta-programming Ruby.

Class variable (@@bar2), IMO is a scoped variable with strange resolving order. By refering @@bar2 in Foo's class scope, it will lookup in the ancestor chain of Foo for the definition of @@bar2, FROM TOP TO BOTTOM. So it will first lookup in BasicObject, then Kernel, then Object, and finally Foo.

Have a play with the following example:

class Foo
  @@bar = 1
  def self.show
    puts @@bar
  end
end

Foo.show  #=> 1

class Object
  @@bar = "hahaha"
end

Foo.show  #=> hahaha
Sign up to request clarification or add additional context in comments.

1 Comment

Better use singleton_class instead of eigenclass since there's even a method with that name
0

Instance variable defined inside the class but outside an instance method called also class instance variable.

Advantage of using class instance variables vs class variables is when you use inheritance. You can use class instance variables in subclasses as well, but it's looks confusing.

It's not exactly an inheritance. Ruby implementation is a little bit tricky. Instance variables in Ruby are created when value assignment happens.

Any subclass can alter the value of shared class variable but class instance variable will be created for particular class only. So pay attention on what behaviour you need for variables in your class.

Disadvantage of class instance variables comparing to class variables is they cannot be used inside instance methods. And in addition to it you can confuse them with regular instance variables.

  class Foo
    @class_instance_var_1 = 0
    @class_instance_var_2 = 0
    @@class_var = 10

    def initialize
      @class_instance_var_1 = 1 # cannot change it here
      @@class_var = 100
    end

    def self.change
      @class_instance_var_2 = 2 # can change here
      @@class_var = 200
    end

    def self.echo_var
      p 'class_instance_var_1=' + @class_instance_var_1.to_s
      p 'class_instance_var_2=' + @class_instance_var_2.to_s
      p 'class_var=' + @@class_var.to_s
    end
  end

  puts '--- initial status of Foo'
  Foo.echo_var
  puts '--- initialized Foo'
  Foo.new
  Foo.echo_var
  puts '--- changed Foo'
  Foo.change
  Foo.echo_var

  class Foo2 < Foo
    @class_instance_var_2 = 22 # can set here only but not in Foo
    @@class_var = 20 # can change here and everywhere
  end

  puts '--- after Foo2 implementation'
  puts '--- Foo'
  Foo.echo_var
  puts '--- Foo2'
  Foo2.echo_var

  class Foo3 < Foo2
    @class_instance_var_2 = 33 # can set here only but not in Foo
    @@class_var = 30 # can change here and everywhere
  end

  puts '--- after Foo3 implementation'
  puts '--- Foo'
  Foo.echo_var
  puts '--- Foo2'
  Foo2.echo_var
  puts '--- Foo3'
  Foo3.echo_var

Outputs this

--- initial status of Foo
"class_instance_var_1=0"
"class_instance_var_2=0"
"class_var=10"
--- initialized Foo
"class_instance_var_1=0"
"class_instance_var_2=0"
"class_var=100"
--- changed Foo
"class_instance_var_1=0"
"class_instance_var_2=2"
"class_var=200"
--- after Foo2 implementation
--- Foo
"class_instance_var_1=0"
"class_instance_var_2=2"
"class_var=20"
--- Foo2
"class_instance_var_1="
"class_instance_var_2=22"
"class_var=20"
--- after Foo3 implementation
--- Foo
"class_instance_var_1=0"
"class_instance_var_2=2"
"class_var=30"
--- Foo2
"class_instance_var_1="
"class_instance_var_2=22"
"class_var=30"
--- Foo3
"class_instance_var_1="
"class_instance_var_2=33"
"class_var=30"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.