【发布时间】:2023-03-07 12:43:01
【问题描述】:
正如每个 Ruby 程序员最终发现的那样,调用包含 return 语句的块或过程可能很危险,因为这可能会退出您当前的上下文:
def some_method(&_block)
puts 1
yield
# The following line will never be executed in this example
# as the yield is actually a `yield-and-return`.
puts 3
end
def test
some_method do
puts 2
return
end
end
test
# This prints "1\n2\n" instead of "1\n2\n3\n"
如果您想绝对确定某些代码在调用块或过程之后运行,则可以使用begin ... ensure 构造。但是由于在yield过程中如果出现异常也会调用ensure,所以需要多做一些工作。
我创建了一个tiny module,它以两种不同的方式处理这个问题:
-
使用
safe_yield,使用return关键字检测yield的块或proc是否实际返回。如果是这样,则会引发异常。unknown_block = proc do return end ReturnSafeYield.safe_yield(unknown_block) # => Raises a UnexpectedReturnException exception -
使用
call_then_yield,您可以调用一个块,然后确保执行第二个块,即使第一个块包含return语句。unknown_block = proc do return end ReturnSafeYield.call_then_yield(unknown_block) do # => This line is called even though the above block contains a `return`. end
我正在考虑用它创建一个快速 Gem,或者是否有任何内置解决方案可以防止从我错过的嵌套块快速返回?
【问题讨论】:
-
这将打破默认的 ruby 行为,带来更多的痛苦,而不是利润。想象一下,我是使用这个技巧的代码的消费者。当我将
return放在我的块中时,我希望它会立即通过控制,如果引发了一些奇怪的异常,我会感到非常惊讶。 -
用干草覆盖狩猎坑只会隐藏陷阱,使代码更难调试。 Ruby 不是一种保护人们免于射腿的语言,而这是它的主要优势。
-
这不是一个好主意,但它是一个好问题。感谢您提出一个有趣的问题。
-
你为什么要传递(甚至创建)一个包含
return语句的proc? -
我,我认为应该完全允许用户在脚上开枪。