Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.9k views
in Technique[技术] by (71.8m points)

why self.method_name cannot access private method ? where as only private method_name can in any method can access private method in ruby

class Person
  def one
    @var = 99
    self.two
  end
  private
  def two
    p @var
  end
end
p=Person.new
p.one

when i run this code code i get error as main.rb:4:in one': private method two' called for #<Person:0x00000000965a28 @var=99> (NoMethodError)
from main.rb:12:in `'

class Person
  def one
    @var = 99
    two
  end
  private
  def two
    p @var
  end
end
p=Person.new
p.one

when i run this i get output as 99. so when the p.one calls it goes to method one execute it when it find method two without any receiver object (which means method two have self as object ) and it successfully executes the method.

But When i manually give self.two it gives me error why ?

what is difference between method call of two VS self.two ?

question from:https://stackoverflow.com/questions/65867395/why-self-method-name-cannot-access-private-method-where-as-only-private-method

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

why self.method_name cannot access private method ?

Because that is how private is defined, at least in the older version of Ruby that you are using.

It can in current versions of Ruby. The behavior you are describing only exists in older versions of Ruby.

If I run your code using my Ruby installation (Ruby 3.0.0 as implemented by YARV 3.0.0), I don't get an error.

Here is how private has historically been defined:

A private method can only be called without a receiver.

See, for example section 13.3.5.3 Private methods of the ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification:

A private method cannot be invoked with an explicit receiver, i.e., a private method cannot be invoked if a primary-expression or a chained-method-invocation occurs at the position which corresponds to the method receiver in the method invocation

Note that this is not the whole story, however.

How do you call a private attribute writer under this rule? The answer is: you can't! You can't use self.foo = bar because you are not allowed to use an explicit receiver and you can't use foo = bar because that is an assignment to the local variable foo and not a call to the attribute writer foo=.

So, there is actually an exception to this rule:

A private method can only be called without a receiver, unless it is an attribute writer, then it can also be called with a receiver that is the literal pseudo-variable self.

Note that it has to be the literal pseudo-variable self. It cannot be any arbitrary expression that evaluates to the same object.

I.e. this is allowed:

self.foo = bar

but this is not:

this = self
this.foo = bar
# private method `foo=' called for #<…> (NoMethodError)

The exact text from the ISO Ruby Language Specification is:

A private method cannot be invoked with an explicit receiver, i.e., a private method cannot be invoked if a primary-expression or a chained-method-invocation occurs at the position which corresponds to the method receiver in the method invocation, except for a method invocation of any of the following forms where the primary-expression is a self-expression.

  • single-method-assignment
  • abbreviated-method-assignment
  • single-indexing-assignment
  • abbreviated-indexing-assignment

However, this still doesn't solve all problems: what about operators, for example self + bar or !self? What about methods whose names are reserved words like class?

The proposed rules became more and more complicated. For some insight, see the following discussion on the Ruby issue tracker: Bug #9907 Abbreviated method assignment with private attr_writer/attr_reader does not work. [Disclaimer: I am the one who filed the bug.]

To make this work, you would have to change the rule to something like

A private method can only be called without a receiver, unless it is an attribute writer or an operator, then it can also be called with a receiver that is the literal pseudo-variable self, including in an abbreviated method assignment.

As you can see, the rule gets rather complex.

A much simpler rule was proposed, and is now implemented as of Ruby 2.7:

The rule now is:

A private method can only be called without a receiver or with an explicit receiver that is the literal pseudo-variable self.

You might ask yourself, why not use this even simpler rule?

A private method can only be called with the receiver self.

The reason is that both the old rule, the in-between rules, and the new rule can be decided statically, in fact even syntactically at parse time, whereas this very simple rule cannot.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...