0

i am trying to understand the .self pointer , class and instance variable and their uses. I found many useful links but nothing seems to get into my head. like this medium post ::

medium.com - @/@@ vs. self in Ruby

here is the code I tried (filename = hello.rb)

class Person
    def instance_variable_get
        @instance_var = "instance variable"
    end

    def class_var
        @@class_var = "class variable"
    end

    def sayhi
        puts "this is a ins #{@instance_var}"
        puts "this is a cls #{class_var}"  #note i removed the @ sign from this.
    end

    def self.sayhi
        puts "this is a ins from class #{@instance_var}"
        puts "this is a cls from class #{@class_var}"
    end
end
bob = Person.new
bob.sayhi
Person.sayhi

and by executing this i got

cd@CD:~/Desktop$ ruby hello.rb 
this is a ins 
this is a cls class variable
this is a ins from class 
this is a cls from class 

how does this all thing work? what am I doing wrong? the result I am expecting from this is

this is a ins instance variable
this is a cls class variable
this is a ins from class instance variable
this is a cls from class class variable

Yakov
  • 3,033
  • 1
  • 11
  • 22
luvji
  • 1
  • 2

2 Answers2

1

That code doesn't actually work since you're not calling any of methods that set the variables. Its kind of a mess and I think I can explain this better with more idiomatic examples.

In Ruby instance variables are just lexically scoped local variables that are scoped to an object instance. They are always "private" but you can provide accessor methods to provide access from the outside (and the insider also):

class Person
  def initialize(name)
    @name = name
  end

  def name=(value)
    @name = name
  end

  def name
    @name
  end

  def hello
    "Hello, my name is #{name}"
  end
end
jane = Person.new("Jane")
puts jane.hello # "Hello, my name is Jane"
jane.name = "Jaayne"
puts jane.hello # "Hello, my name is Jaayne"

Inside the hello method self is the instance of Person that you are calling the the method on.

We can simply call name instead of self.name since its the implicit reciever. We could also write "Hello, my name is #{@name}" and it would give the exact same result since the getter method is just returning the instance variable.

Ending setter methods with = is just a convention that lets you use the method with the = operator. You can actually set instance variables from any method.

class Person
  attr_accessor :name

  def initialize(name)
    @name = name
  end

  def backwardize!
    @name = @name.reverse
  end
end

Class variables on the other hand are a whole different cup of tea. The scope of a class variable is a class - but the are also shared with any subclasses. This is an example of why they are best avoided:

class Vehicle
  def self.number_of_wheels=(value)
    @@number_of_wheels = value
  end

  def self.number_of_wheels
    @@number_of_wheels
  end
end

class Car < Vehicle
  self.number_of_wheels = 4  
end

puts Car.number_of_wheels # 4 - good

class Bike < Vehicle
  self.number_of_wheels = 2  
end

puts Bike.number_of_wheels # 2 - good
puts Car.number_of_wheels # 2 - WAAAT?!

Setting class variables from an instance method which you have done in your example is not commonly done. If you have an instance method that changes the behavior of all other instances of the same class it will often lead to a large amount of swearing.

Instead of class variables use class instance variables:

class Vehicle
  def self.number_of_wheels=(value)
    @number_of_wheels = value
  end

  def self.number_of_wheels
    @number_of_wheels
  end
end

class Car < Vehicle
  # you need to explicitly use self when calling setter methods
  # otherwise Ruby will think you're setting a local variable.
  self.number_of_wheels = 4  
end

puts Car.number_of_wheels # 4 - good

class Bike < Vehicle
  self.number_of_wheels = 2  
end

puts Bike.number_of_wheels # 2 - good
puts Car.number_of_wheels # 4 - good

This can be a mind boggling concept but just try to remember that in Ruby classes are just instances of Class.

You also seem to be somewhat confused about what instance_variable_get whould be used for. Its used to violate encapsulation and get the instance variables of an object from the outside.

class Foo
  def initialize
     @bar = "I'm Foo's secret"
  end
end

puts Foo.new.instance_variable_get(:@bar) # "I'm Foo's secret"

Violating encapsulation should not normally be how you structure your code but it can be very useful in some circumstances. It is not called when accessing instance variables from within an object. I don't think I have ever seen anyone redefine the method - just because you can doesn't mean you should.

max
  • 96,212
  • 14
  • 104
  • 165
  • IMHO that medium article is not very good and is quite confusing. There is actually a bug in it that when ever the author is using `instance_var` you would actually get different string objects unless the code is running with frozen string literals. – max Apr 12 '22 at 17:10
  • thank you @max for the wonderful explanation. now I get the basic idea of self and its use. after reading your answer I thought, I understand everything about class and instance and so i went back and looked at my code . but I couldnt relate what you said in that code. I know that medium article is buggy ,thanks for shouting out loud on that. but still isnt there any way to get what I expected with much less change of code? – luvji Apr 13 '22 at 06:51
0

Because the functions which assign values to variables are not called at all. Do something like this. This ensures the variable values are set when you run a method from object or class:

class Person
    @instance_var = "instance variable"
    @@class_var = "class variable"

    def sayhi
        puts "this is a ins #{@instance_var}"
        puts "this is a cls #{class_var}"  #note i removed the @ sign from this.
    end

    def self.sayhi
        puts "this is a ins from class #{@instance_var}"
        puts "this is a cls from class #{@class_var}"
    end
end
bob = Person.new
bob.sayhi
Person.sayhi

Additionally refer to this: Ruby class instance variable vs. class variable

kawadhiya21
  • 2,458
  • 21
  • 34
  • what if my variables are defined in another function? just like I wrote in the question. or like the code in medium? – luvji Apr 12 '22 at 11:43
  • also running your code gives me the error : Traceback (most recent call last): 1: from hello.rb:16:in `
    ' hello.rb:7:in `sayhi': undefined local variable or method `class_var' for # (NameError) Did you mean? @@class_var
    – luvji Apr 12 '22 at 11:45