Sam Morgan
Sam Morgan

Reputation: 228

Why do private setters behave differently to other private methods?

Here's an interesting case I can't seem to explain. It looks like private setters are 'kind of' private, but sometimes there are exceptions to that. Regular private methods seem to behave differently to private setters:

class TestClass
  def do
    self.foo = :bar # fine
    self.baz        # error
  end

  private

  def foo=(other)
    @foo = other
  end

  def baz
  end
end

TestClass.new.do

The above code sets @foo just fine, despite being called on an explicit self. Then it fails to call #baz, because #baz is a private method.

What's up with this?

Upvotes: 8

Views: 297

Answers (3)

Jörg W Mittag
Jörg W Mittag

Reputation: 369458

Private setters are special because otherwise they couldn't be called at all:

foo = :bar

Assigns to the local variable foo, it doesn't send the message foo=.

Note that setters aren't the only things which cannot be called without an explicit receiver. +@, -@, !, ~, [], []=, +, -, *, /, %, &, |, ^, **, <<, >>, ==, ===, =~, !~, !=, <, >, <=, >=, <=> and probably some others I forgot also cannot be called without an explicit receiver. And then there's stuff like +=, <<= and so on.

There need to be exceptions for all of those. Unfortunately, there are only exceptions for some of those.

It has been proposed to change the rules for private from

Can only be called without an explicit receiver, except for [this long list of exceptions which is really complicated, and still not complete].

to

Can only be called without an explicit receiver or the literal special variable self as receiver.

which preserves all the current properties of the definition (the most important one being that it can be determined statically at parse time).

But so far, nothing has come of it.

Upvotes: 5

Nikita Misharin
Nikita Misharin

Reputation: 2020

That pretty much how the private works and it's the main difference between private and protected methods in ruby. You can't call private methods on instance of the class, even inside that class, while protected allows that.

Upvotes: 0

Sunil D.
Sunil D.

Reputation: 18193

If you don't use self when using the setter method, it will instead assign the value to a local variable. More details.

In the code below, I've removed self from the assignment to foo, and you can see the the setter is not being used b/c @foo is nil:

  def do
    foo = :bar # fine
    baz        # error
    puts @foo  # prints nil
    puts foo   # prints 'bar'
  end

Upvotes: 1

Related Questions