Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby attr_reader and +=

I have recently been stumped by the following code:

class Foo
  attr_accessor :n

  def initialize(i)
    @n = i
  end

  def val
    n
  end

  def bump!
    n += 1
  end
end

f = Foo.new(0)

puts f.val
f.bump!

puts f.val succeeds and prints out 0 as expected. f.bump! causes the following NoMethodError

foo.rb:13:in `bump!': undefined method `+' for nil:NilClass (NoMethodError)
        from foo.rb:20:in `<main>'

Any idea why n is nil in the expression n += 1?

Using n = 1 + n instead raises a TypeError (nil cannot be coerced into Fixnum), so n is in fact nil.

like image 692
Nickolay Kolev Avatar asked Dec 26 '22 17:12

Nickolay Kolev


2 Answers

Even though you've defined an n= method for Foo, Ruby won't let you call it from within the class without an explicit receiver i.e. self.n=

So when you write n += 1, this gets translated into n = n + 1. n= doesn't have an explicit receiver so Ruby creates a local variable n (which is nil). Thus the n in n + 1 refers to a nil local variable, giving you the NoMethodError.

FYI, you don't need attr_accessor unless you want n to be accessible outside of the class! Even then, when you're writing instance methods, you should just use the normal instance variable @n.

like image 52
Max Avatar answered Dec 28 '22 05:12

Max


Your error is there :

def bump!
  n += 1
end

Use self.n. Or @n.

When you do :

attr_accessor :n

In fact you do :

def n
  @n
end

def n=(value)
  @n= value
end

And when you do n += 1, you use a local variable (which is niL) instead of using the two methods that were created by the attr_accessor.

like image 23
Dominic Goulet Avatar answered Dec 28 '22 07:12

Dominic Goulet