Давайте, отметив, что в Ruby, как вы, наверное, знаете, в методе a
заявленных на классе Foo
, я могу ссылаться на защищенные методы на любого экземпляраFoo
.
Как Ruby определяет, есть ли у нас метод, объявленный в классе Foo
? Чтобы понять это, нам придется выкопать внутренности вызова метода. Я буду использовать примеры из версии 2.2 МРТ, но, по-видимому, поведение и реализация одинаковы в других версиях (я бы хотел увидеть результаты тестирования этого на JRuby или Rubinious).
Ruby делает это в rb_call0
. Как следует из комментария, self
используется для определения того, можем ли мы назвать защищенные методы. self
извлекается в rb_call
из информации кадра вызова текущего потока. Затем, в rb_method_call_status
, мы проверяем, что это значение self
имеет тот же класс, на котором определен защищенный метод.
Блоки путают проблему несколько. Помните, что локальные переменные в Ruby-методе захватываются любым блоком, объявленным в этом методе. Это означает, что в блоке self
является тем же self
, на котором был вызван метод. Давайте посмотрим на пример:
class Foo
def give_me_a_block!
puts "making a block, self is #{self}"
Proc.new do
puts "self in block0 is #{self}"
end
end
end
proc = Foo.new.give_me_a_block!
proc.call
Запуск этого, мы видим тот же экземпляр Foo
одинакова на всех уровнях, даже если мы назвали прок от совершенно другого объекта.
Итак, теперь мы понимаем, почему можно вызвать защищенный метод в другом экземпляре того же класса изнутри блока в методе.
Теперь давайте посмотрим, почему proc, созданный с помощью &:bar
, не может этого сделать. Когда мы помещаем знак &
перед аргументом метода, мы делаем две вещи: поручите ruby передать этот аргумент в виде блока и проинструктируйте его называть to_proc
.
Это означает использование метода Symbol#to_proc
. Этот метод реализуется в C, но когда мы вызываем метод C, указатель на self
на текущий кадр становится приемником этого метода C - в этом случае он становится символом :bar
. Итак, мы смотрим на экземпляр foo
, мы получили , как будто мы находимся в методе класса Symbol, и мы не можем вызывать защищенный метод.
Это глоток, но, надеюсь, он имеет достаточный смысл. Дайте мне знать, если у вас есть предложения относительно того, как я могу улучшить его!
Это отличный вопрос. Будем надеяться, что кто-то может просветить нас. –
Хороший вопрос.Единственное отличие, которое я вижу, это то, что вы вызываете «bar» в локальной переменной в первом блоке, но не вызываете его без переменной с proc. Тем не менее, любопытно узнать, есть ли веские основания для этого. – DaniG2k
Протестировано без переменной 'foo', но все равно. Я думаю, что это должно быть связано с тем, как называются procs, хотя я бы предположил, что эти два метода должны выводить эквивалентные результаты. – DaniG2k