Почему переменная с локальной областью действия, которая не была определена, ссылается на переменную экземпляра с тем же именем?
Я обнаружил странную ошибку в моем коде, которая показала интересное поведение 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 в ваших интересах определенно уменьшает мистику и магическую природу некоторого кода.