Reputation: 8424
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
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
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
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