Search code examples
rubysemanticssetter

Why does a Ruby setter return the passed value, rather than the final value of the instance variable?


Ordinarily, Ruby methods return the value of the last expression in the method. For setters of the form name=, however, it seems like

Given the following code:

class Foo
  attr_reader :bar

  def set_bar(v)
    @bar = (v + 1)
  end

  def bar=(v)
    @bar = (v + 1)
  end
end

If I call either bar= or set_bar(), the value of @bar is the same:

f = Foo.new; f.set_bar(1); f.bar
# => 2

f = Foo.new; f.bar=1; f.bar
# => 2

The setters' return values, however, are different:

f = Foo.new; f.set_bar(1)
# => 2
f = Foo.new; f.bar = 1 
# => 1                    
# note: irb in Ruby 2.7+ suppresses this unless you write (f.bar = 1)

This persists even if I add an explicit return:

class Foo
  def bar=(v)
    @bar = (v + 1)
    return @bar
  end
end

f = Foo.new; f.bar = 1 
# => 1

This is surprising (I know, Matz has said explicitly that Ruby doesn't follow the principle of least surprise) and it's not obviously documented anywhere. Is it documented? Is there a reason for it?


Solution

  • All assignments always evaluate to the right hand side. It doesn't matter whether it's a

    • local variable assignment:

      foo = 42 #=> 42
      
    • instance variable assignment

      @foo = 42 #=> 42
      
    • class variable assignment

      @@foo = 42 #=> 42
      
    • global variable assignment

      $foo = 42 #=> 42
      
    • constant assignment

      FOO = 42 #=> 42
      
    • abbreviated assignment

      foo += 42 #=> 42
      foo -= 42 #=> 42
      foo *= 42 #=> 42
      foo /= 42 #=> 42
      foo %= 42 #=> 42
      foo <<= 42 #=> 42
      foo >>= 42 #=> 42
      foo |= 42 #=> 42
      foo &= 42 #=> 42
      foo ||= 42 #=> 42
      foo &&= 42 #=> 42
      
    • method assignment

      foo.bar = 42 #=> 42
      

    Note that the return value is not completely ignored:

    f.public_send(:bar=, 1)
    #=> 2
    

    Is it documented?

    My favorite piece of documentation for questions like this is the ISO/IEC 30170:2012 Information technology — Programming languages — Ruby specification. The section you are looking for is 11.4.2.2.5 Single method assignments.

    Is there a reason for it?

    Consistency and Principle of (matz's) Least Astonishment: all other assignments evaluate to the right-hand side in Ruby. In fact, in pretty much all languages where assignments are expressions, the assignments evaluate to the right-hand side.

    So, it makes sense for method assignments to also evaluate to their right-hand side.