Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Ruby

When do I use @ and when is it not necessary?

So I read this discussion [https://teamtreehouse.com/community/why-do-i-have-to-use-here] about the instance variables but I still don't get, when do I have to use @. I post here two codes, which seem quite similar to each other, but one works without @ and the other does not. In the code below, on line 10, @health wouldn't work without @, even though it's set in attr_accessor:

class Monster
    attr_accessor :name, :health

 def initialize(name)
        @name = name
        @health = 100
  end

   def hit(amount)
        @health -= amount
   end

end

dragon = Monster.new("Smaug")
dragon.hit(25)
puts dragon.inspect

I changed the code a bit and made @health an empty array and called push on it. Suddenly line 10 works without @ (but @health works too). What is the difference? Why one works and the other does not? What is the logic behind? And does attr_accessor have anything to do with it? Thanks for explanation.

class Monster
   attr_accessor :name, :health

    def initialize(name)
        @name = name
        @health = []
    end

    def hit(amount)
      health.push(amount)
    end

end

dragon = Monster.new("Smaug")
dragon.hit("25")
puts dragon.name
puts dragon.inspect

2 Answers

Maciej Czuchnowski
Maciej Czuchnowski
36,441 Points

OK, I'll try to explain this. First of all, you have to understand what attr_accessor does. when you write:

class Monster
   attr_accessor :name
end

what ruby does under the hood is that it effectively creates two methods that look like this:

class Monster
   def name
      @name
   end
end

yes, this is all it does - it creates two methods that allow you to READ and WRITE this instance variable. So if you have this instance variable there, the accessor lets you do these two things with it.

Now, what is an instance variable? In short, it is a variable that retains its value when you move to another part of the code. When you have a method like this:

def some_method
   some_variable = 5
end

some_variable is a local variable - once you leave this method's code, you cannot read or change its value. If you did anything with this variable inside this method, once you leave it, the whole thing is lost. On the other hand, if you make it an instance variable, like this:

def some_method
   @some_variable = 5
end

you will be able to access this @some_variable variable in other parts of the code. If you do stuff to this variable, it's value will change, and updated version will be see in other parts of the code.

So, in your first example, you create Smaug - he is an instance of the Monster class. His two instance variables are @name and @health. Since we used accessors, we are able to read and modify these two instance variables in a simple way - dragon.name, dragon.name = ''Lofwyr" etc. When you use the #hit method, you simply subtract some value from this instance variable. Because it's an instance variable, it's value is remembered later on, which you can check by hitting the dragon a few times and then checking its health. With local variables this is not possible, since they are removed from memory once you stop executing code where they were situated - they have LOCAL scope.

Does that help?

Thanks for the explanation. I get now why in the first example there's a need for the instance variable. But what about the second example, why is there no need for the instance variable and the code works without it?

As a side note, I found this confusing but i think its quite important. When you use accessors ruby is essentially creating getter and setter methods.

The reason your first example wont work without the '@' is because ruby is assuming you are trying to set a local variable. This is why it gives you a NilClass error. It thinks you are trying to create a local var called health and call -= 25 on it. Which wont work.

You have to be explicit that you are subtracting the integer from the health of this instance of the Monster class. So "hit" is basically using a setter method to adjust the health of your monster. So this would work...

def hit(amount)
    self.health -= amount
end
Maciej Czuchnowski
Maciej Czuchnowski
36,441 Points

The second example KINDA works (since it doesn't do what you'd expect - it does not decrease dragon's base health at all, since it doesn't even have any health). It works because you DID create an instance variable @health and you are referring to it through health accessor. So you are still pushing stuff to an instance variable, although this is not noticeable at first sight, since health method is not overt - it's hidden in the accessor.