Ruby 方法参数默认值、隐式与文字 nil、库的责任还是调用者?



我希望你们能帮助我决定哪种行为更"标准"或"预期"。

背景:

在 Ruby 中,您可以为方法提供默认参数,例如。

def tester(input={})
  input
end

如果你打电话给tester你会得到{}.但是,如果您打电话给tester(nil),则会nil

老实说,这让我感到惊讶,我认为如果您将nil传递给该方法,您会得到{},因为我总是认为方法定义中input={}语句类似于input ||= {},但显然它更像是defined?(input) ? input : {}

问题:

在我维护的一个库中,我有一个类,它在初始化时采用可选的哈希。初始值设定项看起来就像上面的测试器一样:

def initialize(input={})

所以你可以打电话给MyClass.newMyClass.new foo:1,但你不能打电话给MyClass.new nil

这似乎很明显,除非使用变量进行初始化,例如。 MyClass.new(opts) opts 可以是哈希或 nil。

我可以通过更改实现来更改其工作方式,如下所示:

def initialize(input=nil)
  input ||= {}
  ...
end

但是,我想知道,这是Ruby界面的正确设计吗?

我应该期望调用者使用MyClass.new(opts || {})吗?

如果您使用的是库,并且该库中有一个接受可选哈希的类,您是否希望将nil传递给初始值设定项并将其视为等效于不传递参数是安全的?或者你会期望它中断,因为在 Ruby 中,传递字面上的 nil 与传递任何参数不同?

哪种设计对您来说更"正确"?

默认值仅适用于未指定的参数,不适用于提供的参数,而是nil 。在Ruby中,这是一个值,甚至是一个对象,它不像其他语言那样带有"undefined"或"null"。

你对使用||= { }的观察是许多库如何处理这种事情。我通常设计代码在面对意外传递nil时具有弹性,因为这可能会错误地发生,并且似乎是对如何做我的意思而又不过分偏执和防御的合理解释。

通常,这只是= { }= [ ]类型情况下的问题。从某种意义上说,这是Ruby原则,即传入足够接近工作的东西,或者至少不是不正确的。想想如何使用该方法并适应所有合理情况。

因此,您倾向于看到的签名如下所示:

def with_options(args = nil)
  # Default arguments to an empty Hash
  args ||= { }
  # ...
end
def trim(string, length = 1)
  string = string.to_s # Coerce to string
  length = length.to_i # Coerce to integer
  # ...
end
对于

内部使用的代码,通常最好更严格,如果方法使用不正确,则抛出错误,即防御性较低,但对于可能不能使所有内容完全正确的各方使用的方法,始终赞赏一定程度的自由度。

在像 Rails 这样的环境中,传递"1"而不是1进行数值论证太容易了,惩罚某人没有正确投射可能太残忍了。至少尝试转换并处理结果。

最新更新