在 Ruby 2.6 及之前的版本中,**argument 语法主要(但不完全)是传递哈希的语法糖。这样做是为了保持将变量散列作为最后一个参数传递给有效方法的约定。
在 Ruby 2.7 中,关键字参数在语义上被更新,不再映射到哈希参数。在这里,关键字参数是从位置参数处理的。
在 Ruby 2.6 及之前版本中,以下两个方法定义(至少在许多方面)等效:
def one(args={})
#...
end
def two(**args)
#...
end
在这两种情况下,您都可以传递具有相同结果的逐字散列或散点散列:
arguments = {foo: :bar}
one(arguments)
one(**arguments)
two(arguments)
two(**arguments)
然而,在 Ruby 2.7 中,您应该按原样传递关键字参数(之前的行为仍然有效,但已被警告弃用)。因此,对two(arguments) 的调用将在 2.7 中导致弃用警告,并在 Ruby 3.0 中无效。
在内部,散列散列参数(将关键字参数传递给方法)因此在 Ruby 2.7 中会导致空的关键字参数列表,但在 2.6 中会导致带有空散列的位置参数。
您可以通过验证 Ruby 如何解释其public_send 方法的参数来详细了解此处发生的情况。在 Ruby 2.6 及更早版本中,该方法实际上具有以下接口:
def public_send26(method_name, *args, &block);
p method_name
p args
# we then effectively call
# self.method_name(*args, &block)
# internally from C code
nil
end
当在 Ruby 2.6 中以 public_send26(:a, **{}) 调用此方法时,您将看到关键字参数再次“包装”在哈希中:
:a
[{}]
在 Ruby 2.7 中,您拥有以下有效接口:
def public_send27(method_name, *args, **kwargs, &block);
p method_name
p args
p **kwargs
# Here, we then effectively call
# self.method_name(*args, **kwargs, &block)
# internally from C code
nil
end
您可以看到,关键字参数在 Ruby 2.7 中作为关键字参数单独处理和保留,而不是像在 Ruby 2.6 及更早版本中那样作为方法的常规位置哈希参数处理。
Ruby 2.7 仍然包含回退行为,因此预期 Ruby 2.6 行为的代码仍然有效(尽管带有警告)。在 Ruby 3.0 中,您必须严格区分关键字参数和位置参数。您可以在a news entry on ruby-lang.org 中找到有关这些更改的更多说明。