Ruby 2.3+能否将安全导航运算符&.别名化为自定义方法?
实现自定义
fast_try替代安全导航运算符&. 好问题!我之前也纠结过安全导航运算符&.的可读性问题,同时又想保留它的性能优势,刚好可以给你一套可行的方案。
核心实现思路
我们可以通过给Object和NilClass添加自定义方法的方式,模拟&.的行为,同时实现类似try的语法风格。代码如下:
# 给所有非nil对象添加fast_try方法,直接调用目标方法 class Object def fast_try(method_name, *args, &block) send(method_name, *args, &block) end end # 给nil对象覆盖fast_try,直接返回nil class NilClass def fast_try(*) nil end end
这样你就可以用s.fast_try(:upcase!).fast_try(:downcase)替代s&.upcase!&.downcase,效果基本一致。
性能优势
这个实现比ActiveSupport的try方法性能更高:
- ActiveSupport的
try(不带!)会额外做方法存在性检查,还有参数解析的逻辑,开销更大; - 我们的
fast_try只是简单的send调用和nil返回,逻辑极简,性能非常接近原生的&.(仅比&.多一层方法调用的微小开销)。
需要注意的限制
正如你所说,不在意细微差异,但这些关键点还是要明确:
- 方法不存在时的行为:和
&.一样,如果目标方法不存在,fast_try会直接抛出NoMethodError;而ActiveSupport的try(不带!)会返回nil,这是最显著的差异。 - 私有方法调用:
fast_try通过send可以调用私有方法,但&.无法调用私有方法(会抛出NoMethodError),如果你的代码里有私有方法调用场景,需要特别注意。 - 语法简洁性:
&.可以直接写s&.upcase,而fast_try必须写成s.fast_try(:upcase),需要传入方法符号,写法上多了一点冗余。 - 块调用支持:虽然代码里支持块,但
&.调用带块的方法时写法更自然(比如s&.each { ... }),而fast_try需要写成s.fast_try(:each) { ... },体验稍差。
总结
如果你能接受上述限制,这个fast_try实现完全可以满足你的需求——既拥有类似try的明确可读性,又获得了接近原生安全导航运算符的性能表现。
内容的提问来源于stack exchange,提问作者Weston Ganger
相关产品推荐
相关产品推荐

