Why is a Ruby setter method returning a String rather than a Symbol as the last expression evaluated?

£可爱£侵袭症+ 提交于 2019-12-22 18:13:09

问题


Unexpected Return Value from Method: Expecting Symbol

I have the following Ticket class:

class Ticket
  VALID_STATES = %i[open closed invalid wontfix]
  attr_reader :status
  def status= new_state
    new_state = new_state.to_sym
    @status = new_state
  end
end

When passed a String rather than a Symbol, the setter method unexpectedly returns a String, even though the correct value is being returned by the getter method. For example:

t = Ticket.new
t.status = 'closed'
#=> "closed"

t.status
#=> :closed

It looks like the correct value is being stored as a Symbol, but I'm at a loss as to why the method is returning "closed" at the REPL when the last expression evaluated should return :closed. My expectation is that the expression in question ought to resolve as @status = :closed, and therefore should return a Symbol.

Can anyone explain why I'm getting a String rather than a Symbol as the return value from the setter method?

Caveats and Bike-Shedding Prevention

  1. I know this example could just use @status = new_state.to_sym rather than assigning back to new_state, but there's intermediate code that was removed to create this minimal example. I didn't want to change the code too much, as that defeats the point of showing what my real code is doing. It doesn't seem to make a difference for this specific problem anyway; I've tried it both ways.
  2. I tried this with Ruby 2.3.1, 2.4.0-preview2, and JRuby 9.1.4.0, so it's not version-specific.
  3. Various debugging attempts ran afoul of other issues specific to the REPL's toplevel in both Pry and IRB, which I will open as a separate question. The point here is that trying to debug with alternative abstractions like def foo=(str); @foo = str.to_sym; end leads further down the rabbit hole.
  4. It's extremely possible that the problem exists between the keyboard and the chair, but the focus of the question is really about why the return value is not of the expected class.

回答1:


It's expected. From the documentation:

Note that for assignment methods the return value will always be ignored. Instead, the argument will be returned:

def a=(value)
  return 1 + value
end

p(a = 5) # prints 5

Ruby allows you to chain assignments:

foo = bar = 'closed'

The above assigns "closed" to both, foo and bar.

Returning the argument and ignoring the method's return value lets you replace bar with a method call:

foo = t.status = 'closed'

IMO it would be quite surprising if the above would assign :closed to foo.

If you really want the return value, use send or public_send:

def a=(value)
  return 1 + value
end

p(a = 5)        # prints 5
p(send(:a=, 5)) # prints 6



回答2:


The answer is rather simple: in Ruby, assignments evaluate to the value being assigned. There's nothing special about method assignments.



来源:https://stackoverflow.com/questions/40421090/why-is-a-ruby-setter-method-returning-a-string-rather-than-a-symbol-as-the-last

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!