Bug #19795
closedattr_accessor leading to nil values for re-assignment
Description
Steps to reproduce:¶
- Create a class with an
attr_accessor
for an instance variable - Create an instance method that reassign this variable using the current value stored in the variable
- Show that the variable is set to nil during the evaluation
Code snippet:
# attr_accessor_nil.rb
class A
attr_accessor :a
def initialize
@a = 0
end
def my_method
puts "a is '#{a.inspect}' of class '#{a.class}'"
a += 1
end
end
instance = A.new
instance.my_method
# output:
#
# a is '0' of class 'Integer'
# attr_accessor_nil.rb:12:in `my_method': undefined method `+' for nil:NilClass (NoMethodError)
#
# a += 1
# ^
# from attr_accessor_nil.rb:17:in `<main>'
Expected behavior¶
a += 1
should lead to a
being equal to 1
at the end of the assignment, because a
was storing 0
previously, as shown by the puts
. Am I being wrong expecting this result?
Actual behavior¶
a += 1
raises an error about a
being nil
in the evaluation.
Further investigation¶
I checked if it was coming from the "instance variable", or about the "attr_accessor" by running the following snippet:
Code snippet:
# attr_accessor_nil.rb
class A
attr_accessor :a
def initialize
@a = 0
end
def my_method
puts "a is '#{a.inspect}' of class '#{a.class}'"
@a += 1 # use the instance variable directly, instead of the accessor
end
end
instance = A.new
instance.my_method
# output:
#
# a is '0' of class 'Integer'
This snippet runs just fine, and no error is raised.
System configuration¶
Ruby version : 3.2.2
Updated by francktrouillez (Franck Trouillez) over 1 year ago
- Description updated (diff)
Updated by nobu (Nobuyoshi Nakada) over 1 year ago
- Status changed from Open to Closed
This is an assignment to the local variable, not the accessor.
a += 1
You have to write:
self.a += 1
Updated by francktrouillez (Franck Trouillez) over 1 year ago
I guess that it is a local variable, but is it the expected behavior?
As a developer, I expect that I can access a
without the self.a
.
Since the following code works as expected without self
:
# attr_accessor_nil.rb
class A
attr_accessor :a
def initialize
@a = 0
end
def my_method
puts "a is '#{a.inspect}' of class '#{a.class}'"
b = a + 1
a = b
puts "b is '#{b.inspect}' of class '#{b.class}'"
puts "a is '#{a.inspect}' of class '#{a.class}'"
end
end
instance = A.new
instance.my_method
# output:
#
# a is '0' of class 'Integer'
# b is '1' of class 'Integer'
# a is '1' of class 'Integer'
I expect that the following works as well:
# attr_accessor_nil.rb
class A
attr_accessor :a
def initialize
@a = 0
end
def my_method
puts "a is '#{a.inspect}' of class '#{a.class}'"
a = a + 1
end
end
instance = A.new
instance.my_method
# output:
#
# a is '0' of class 'Integer'
# Traceback (most recent call last):
# 1: from attr_accessor_nil.rb:17:in `<main>'
# attr_accessor_nil.rb:12:in `my_method': undefined method `+' for nil:NilClass (NoMethodError)
Why can I access the instance variable (read and write) without the self
, but as soon as I try to re-assign it using the current value, it switches to a local variable? This is something unexpected for me.
Is it the expected behavior for this? If so, is there a way of making this special case clearer? Or am I being wrong thinking the behavior is unexpected?
Updated by francktrouillez (Franck Trouillez) over 1 year ago
I got it in the end thanks to a peer. I misunderstood wrongly how this was working:
Running the following snippet helped me understand:
# attr_accessor_nil.rb
class A
attr_accessor :a
def initialize
@a = 0
end
def my_method
puts "a is '#{a.inspect}' of class '#{a.class}'"
b = a + 1
a = b
puts "b is '#{b.inspect}' of class '#{b.class}'"
puts "a is '#{a.inspect}' of class '#{a.class}'"
end
def my_method_2
puts "a is '#{a.inspect}' of class '#{a.class}'"
end
end
instance = A.new
instance.my_method
instance.my_method_2
# output:
#
# a is '0' of class 'Integer'
# b is '1' of class 'Integer'
# a is '1' of class 'Integer'
# a is '0' of class 'Integer'
So, we cannot modify the instance variable through the accessor directly. If it was, the last puts
should show 1
Thanks for your help!