Почему переменная с локальной областью действия, которая не была определена, ссылается на переменную экземпляра с тем же именем?

Я обнаружил странную ошибку в моем коде, которая показала интересное поведение ruby. Надеюсь, кто-то может объяснить, почему он так себя ведет.

У меня был класс с переменной экземпляра @foo и метод, который ссылался на локальную переменную scoo. Я рефакторил часть метода, случайно оставив ссылку на foo; переменная больше не определена в области видимости. Вместо этого он указывал на @foo. Изменение foo внесло изменения в @foo и наоборот.

Упрощенная версия: РЕДАКТИРОВАТЬ: добавлено ImOutOfNames.

class ImOutOfNames
    attr_accessor :foo # the culprit!
end

class Bar < ImOutOfNames
    def initialize
        @foo = "that is a tasty burger"
    end

    def bar_method_1
        foo = "Come on Yolanda, whats Fonzie like?"
        bar_method_2
    end

    def bar_method_2
        puts foo
    end
end

И результат bar_method_1 и bar_method_2 был "это вкусный гамбургер". Я ожидал, что будет ошибка, например, выполнение приведенного выше кода получает

NameError: undefined local variable or method

Я даже пригласил более старшего разработчика, и он был несколько озадачен и подтвердил поведение.

Это ожидаемое поведение, и я неправильно понял, как работает @variables или что-то не так?

2 ответа

Решение

Ваш предыдущий ошибочный код, вероятно, в определении attr_accessor, который создал метод foo, который получил доступ к вашей переменной экземпляра,

Вы можете иметь такое же поведение, если ваш код выглядит так:

class Bar

  attr_accessor :foo

  def initialize
    @foo = "that is a tasty burger"
  end

  def bar_method_1
    foo = "Come on Yolanda, whats Fonzie like?"
    bar_method_2
  end

  def bar_method_2
    puts foo
  end
end

Вызов attr_accessor определяет два метода в вашем объекте;

def foo
  @foo
end

def foo=(value)
  @foo = value
end

Таким образом, в вашем случае, когда локальная переменная не была определена, метод использовался, но, поскольку вы не вызывали attr_accessor в этом примере, который вы опубликовали, метод не был определен, и не было локальной переменной для использования в bar_method_2, поэтому вызов не удался.

У вас есть аксессор attr для вашей переменной экземпляра @foo? Это один из способов, которым это может произойти.

В общем, я бы рекомендовал не использовать одно и то же имя для локальной переменной, но вы можете узнать, происходит ли оно от какого-то метода с

self.method(:foo)

или если это даже определено с

defined? foo

Использование среды исполнения ruby ​​в ваших интересах определенно уменьшает мистику и магическую природу некоторого кода.

Другие вопросы по тегам