为什么 Ruby setter 方法返回的是字符串而不是符号作为最后计算的表达式?

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

意外的Return方法的值:期望符号

我有以下票证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

当传递字符串而不是符号时,setter 方法意外地 return 是一个字符串,即使 return 方法getter 编辑了正确的值方法。例如:

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

t.status
#=> :closed

看起来正确的值正在 存储 作为符号,但我不知道为什么该方法是 returning "closed" 在 REPL 中最后一个表达式求值应该 return :closed。我的期望是所讨论的表达式应该解析为 @status = :closed,因此应该 return 一个符号。

谁能解释为什么我从 setter 方法中得到一个字符串而不是符号作为 return 值?

警告和自行车脱落预防

  1. 我知道这个例子可以只使用 @status = new_state.to_sym 而不是分配回 new_state,但是删除了中间代码来创建这个最小的例子.我不想过多地更改代码,因为这违背了显示我的真实代码在做什么的意义。无论如何,它似乎对这个特定问题没有影响;两种方法我都试过了。
  2. 我用 Ruby 2.3.1、2.4.0-preview2 和 JRuby 9.1.4.0 试过了,所以它不是特定于版本的。
  3. 各种调试尝试 运行 与 Pry 和 IRB 中 REPL 顶层的其他特定问题发生冲突,我将作为一个单独的问题打开。这里的要点是,尝试使用 def foo=(str); @foo = str.to_sym; end 等替代抽象进行调试会导致进一步陷入困境。
  4. 极有可能键盘和椅子之间存在问题,但问题的重点实际上是关于为什么return值不是预计 class.

这是意料之中的。来自 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 允许您链式赋值:

foo = bar = 'closed'

以上将 "closed" 分配给 foobar

返回参数并忽略方法的 return 值让您可以用方法调用替换 bar

foo = t.status = 'closed'

IMO 如果上面将 :closed 赋值给 foo.

,那将是非常令人惊讶的

如果您真的想要 return 值,请使用 sendpublic_send:

def a=(value)
  return 1 + value
end

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

答案很简单:在 Ruby 中,赋值求值为被赋值。方法赋值没什么特别的。