daremkd
daremkd

Reputation: 8424

Why isn't a variable defined by attr_accessor available in a method?

class A
 attr_accessor :rank
 def change_rank
   rank = rank + 1
 end
end

a = A.new
a.rank = 5
p a.rank
a.change_rank
p a.rank

produces an error for rank = rank + 1 (undefined method + for nil:Nilclass). Shouldn't an implicit call to the "rank" method return the value of the instance variable @rank? For some reason, this code works if I change the 4th line to:

self.rank = self.rank + 1

Why does an explicit call to the rank method works while an implicit one doesn't?

Upvotes: 1

Views: 228

Answers (2)

uncutstone
uncutstone

Reputation: 408

I rewrite your code as following. You can see how Ruby treats rank= and rank with different ways.

class A
  attr_accessor :rank
  def change_rank
    self.rank = rank + 1
 end
end

a = A.new
a.rank = 5
p a.rank
a.change_rank
p a.rank

execution result:

5
6

Upvotes: 0

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230336

 def change_rank
   rank = rank + 1
 end

In this context name rank does not resolve to one of methods generated by attr_accessor. It is seen by ruby roughly as this:

 def change_rank
   rank = nil
   rank = rank + 1
 end

When ruby sees an assignment to a "naked" name, it creates a local variable with this name. It will shadow the outer method. And since local vars are initialized with nil, it explains the error you're getting.

You can do this to avoid the error (be explicit about what you're modifying):

 def change_rank
   self.rank += 1
 end

Update:

Here's more code that illustrates this

defined?(x) # => nil # name x is unidentified yet
defined?(x = x) # => "assignment" # local var x is created by assignment
defined?(x) # => "local-variable" # now local var x exists
x # => nil # and its value is nil

Upvotes: 5

Related Questions