SOLO
SOLO

Reputation: 868

Instance var not recognized if declared in class but does work in method

I'm brand-new to Ruby coming from a Java background. I don't understand the relationship between instance methods and variables yet, apparently. I just had some code like this:

class Problem022
  FILENAME = "resources/p022_names.txt"
  @name_array = [] # <--- class line 3

  def do_problem
    read_input()
    sort_input()

    # do more stuff
  end

  def read_input
    # <--- read_input line 2

    File.open(FILENAME, "r") do |file|
      file.each_line do |line|
        scanner = StringScanner.new(line)

        while scanner.exist?(/,/) do
          name = scanner.scan_until(/,/)

          @name_array.push(name) # <--- marked line
        end

    # more logic
  end

  def sort_input()
    @name_array.sort!
  end

  # other methods
end

puts problem = Problem022.new.do_problem()

I confirmed this worked correctly up until "marked line" (see comment above), but on that line, I got the error

in `block (2 levels) in read_input': undefined method `push' for nil:NilClass (NoMethodError)

I was able to solve it by (kinda blindly) adding the line

@name_array = []

at "read_input line 2", but I don't understand why this worked. Why doesn't the method recognize the declaration of instance variable @name_array in the class body? It seems to be able to use the constant FILENAME just fine, and that's defined in basically the same place. The method and the variable are both instance, not class, so I wouldn't expect any problems there. I just realized that the program still runs fine even if the declaration on "class line 3" is commented out, which only confuses me more.

I've read multiple definitions of scope in Ruby but none of them cleared this up for me. What am I misunderstanding?

Upvotes: 0

Views: 173

Answers (1)

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230521

Here's some code that should illuminate what's going "wrong".

class Problem
  @name = 'foo' # this belongs to the class, because that's what current `self` is.

  # instance method
  # inside of this method, `self` will be an instance of the class
  def action
    @name
  end

  # class method (or class instance method)
  # inside of this method, `self` will refer to the class
  def self.action
    @name
  end
end

p1 = Problem.new # an instance of the class
p2 = Problem # the class itself

p1.action # => nil, you never initialize @name in the context of class instance
p2.action # => "foo"

Key points:

  • classes are objects and can have instance variables too. These variables are not in any way related to those of instances of the class.
  • concept of "current self" is one of ruby's fundamentals (related to "implicit receiver method calls"). You won't get very far without understanding it.

Upvotes: 2

Related Questions