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?
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.