модификаторы доступа ruby, разные выходные данные с разными версиями 2.5 — 2.7

#ruby #access-modifiers

Вопрос:

Я пытаюсь запустить этот код, и он выдает разные результаты с разными версиями ruby 2.5 — 2.7

код:

 class ParentClass
    def the_public_method
        self.method1
    end

    private

    def method1
        puts "The private has been called"
    end
end

class ChildClass < ParentClass 
    def test
        self.method1
    end
end

ParentClass.new.the_public_method 
ChildClass.new.test
 

на ruby 2.5 это дает:

 Traceback (most recent call last):
    1: from main.rb:19:in `<main>'
main.rb:3:in `the_public_method': private method `method1' called for 
#<ParentClass:0x000056367ee0b388> (NoMethodError)
Did you mean?  method
               methods
exit status 1
 

на ruby 2.7 это дает:

 The private has been called
The private has been called
 

Я думаю, что первый вывод верен для более старой версии ruby..
есть какие-нибудь отзывы?

Ответ №1:

Я думаю, что первый вывод верен для более старой версии ruby.. есть какие-нибудь отзывы?

Оба вывода верны. Спецификация была изменена в Ruby 2.7, поэтому, естественно, Ruby 2.7 ведет себя по-другому.

Первоначально правилом для частных методов было «частные методы могут вызываться только без явного получателя».

Однако это означает, что вы не можете использовать частные установщики, поскольку foo = :bar это назначение локальной переменной и self.foo = :bar не разрешено.

Таким образом, правило было изменено на «частные методы могут вызываться только без явного получателя, за исключением установщиков, где self в качестве получателя допускается литеральная псевдопеременная».

Но это все еще не учитывает такие вещи, как self 2 или self.foo = 2 где foo , или foo= являются частными , и многие, многие другие угловые случаи.

Некоторое время разработчики Ruby пытались справиться с этим, либо игнорируя некоторые из этих угловых случаев, либо добавляя все более сложный набор исключений, но на самом деле решение довольно простое: измените правило на «частные методы можно вызывать только с буквальной псевдопеременной self в качестве явного или неявного получателя».

И это правило действует со времен Ruby 2.7.

Комментарии:

1. К сожалению, угловой случай obj = self ; obj.foo все еще терпит неудачу.

2. Одна вещь, которая объединяет все эти определения, заключается в том, что их можно синтаксически проверить. Несколько разработчиков (я лично знаю о JRuby и TruffleRuby) очень хотели сохранить это свойство, что было верно с тех пор, как в Ruby существовали частные методы. Всегда требовалось, чтобы только литеральная псевдопеременная self могла использоваться в качестве явного приемника для частных методов. В противном случае вы столкнетесь с такими проблемами, как def foo; if rand < 0.5 then self else Object.new end end; foo.bar : теперь , если bar это личное, разрешено это или нет?

3. Да, тогда вам придется проверить вызов. Но я не понимаю, почему это плохо.

Ответ №2:

Ruby 2.7 позволяет вызывать частный метод с помощью self

До Ruby 2.7 вызов частного метода записи/назначения с буквальным self в качестве получателя был разрешен, но вызов любого другого частного метода с self приводил к ошибке nometoderror.

Ruby 2.7 нацелен на стандартизацию взаимодействия между собственными и частными методами. Вышеуказанное несоответствие было исправлено в Ruby 2.7.

Таким образом, первый вывод верен до Ruby 2.7, второй вывод верен после Ruby 2.7.

Ответ №3:

В Ruby private метод по-прежнему доступен из унаследованных классов, но используется для требования неявного полученного (т. Е. неявного вызова, например mehtod1 , но не obj.method1 или self.method1 )

Как заявил @eux, это последнее требование было смягчено в ruby 2.7, так что теперь вы self.method тоже можете звонить.


Еще одна особенность видимости в Ruby заключается в том, что она связана с экземплярами, а не с самим классом. Это объясняет поведение private и позволяет вам понять следующий код:

 class Foo
  def initialize(name)
    @name = name
  end

  def ==(rhs)
    name == rhs.name
  end

  private

  attr_reader :name
end

f = Foo.new("bar")

f == f # NoMethodError
 

Здесь возникает ошибка nometoderror, потому attr_reader :name что она закрыта, поэтому вы не можете получить доступ к методу name другого объекта. Чтобы включить такое поведение, используйте protected