Total newbie: Instance variables in ruby?

instance-variables, ruby

Solution

Let's walk through the code, shall we?

#bowling.rb

class Bowling
  @game_score = 0 # (1)

At this point (1), we are still inside the class `Bowling`. Remember: classes are just objects like any other. So, at this point you are assigning `0` to the instance variable `@game_score` of the class object `Bowling`.

 def hit(pins)
  @game_score = @game_score + pins # (2)

Now (2), we are inside an instance method of the `Bowling` class. I.e.: this is a method that is going to belong to an instance of `Bowling`. So, now the instance variable `@game_score` belongs to an instance of the `Bowling` class, and not to the class itself.

Since this instance variable is never initialized to anything, it will evaluate to `nil` (in Ruby, uninitialized variables always evaluate to `nil`), so this evaluates to `@game_score = nil + pins` and since `nil` doesn't have a `#+` method, this will result in a `NoMethodError` exception being raised.

 end
 def score
  @game_score # (3)

And here (3), we are again inside an instance method of the `Bowling` class. This will always evaluate to `nil`, for the reason I outlined above: `@game_score` is never initialized, therefore it evaluates to `nil`.

 end
end

We can use Ruby's reflection capabilities to take a look at what's going on:

p Bowling.instance_variable_get(:@game_score) # => 0
b = Bowling.new
p b.instance_variable_get(:@game_score) # => nil

Now let's inject a value into the instance variable:

b.instance_variable_set(:@game_score, 1)
p b.score # => 1
b.hit(3)
p b.score # => 4

So, we see that everything works as it should, we only need to figure out how to make sure the instance variable gets initialized.

To do that, we need to write an initializer method. Strangely, the initializer method is actually a private instance method called `initialize`. (The reason why `initialize` is an instance method and not a class method, is actually quite simple. Ruby splits object creation in two phases: memory allocation and object initialization. Memory allocation is done by a class method called `alloc` and object initialization is done by an instance method called `initialize`. (Objective-C programmers will recognize this.) The reason why `alloc` is a class method is simply that at this point in the execution there is no instance yet. And the reason that `initialize` is an instance method is that object initialization is obviously per-object. As a convenience, there is a standard factory class method called `new` that calls both `alloc` and `initialize` for you.)

class Bowling
 def initialize
  @game_score = 0
 end
end

Let's test this:

c = Bowling.new
p c.score # => 0
c.hit(2)
p c.score # => 2

BTW: just some minor Ruby style tips: indentation is 2 spaces, not 1 tab. And your `hit` method would more idiomatically be `@game_score += pins`.

Problem

Pardon the total newbiew question but why is @game_score always nil? ``` #bowling.rb class Bowling @game_score = 0 def hit(pins) @game_score = @game_score + pins end def score @game_score end end ```

Original source