当使用Ruby`yield时,如何防止“return`from block”出现问题`
正如每个Ruby程序员最终发现的那样,调用包含当使用Ruby`yield时,如何防止“return`from block”出现问题`,ruby,yield,Ruby,Yield,正如每个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 som
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。。。确保构造。但是,由于如果在yield期间出现异常,也会调用sure
,因此需要做更多的工作
我创建了一个以两种不同方式处理此问题的解决方案:
使用safe\u yield
,使用return
关键字检测生成的块或过程是否实际返回。如果是这样,它会引发一个异常
unknown_block = proc do
return
end
ReturnSafeYield.safe_yield(unknown_block)
# => Raises a UnexpectedReturnException exception
使用call\u then\u 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,或者是否有任何内置的解决方案来防止我错过的嵌套块的快速返回?有一个内置的解决方案来检测块是否包含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
您可以使用来分解用户传入的块,然后在其中搜索表示return
语句的throw 1
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
下面是一个示例实现:
def safe_yield(&block)
if RubyVM::InstructionSequence.disasm(block) =~ /^\d+ throw +1$/
raise LocalJumpError
end
block.call
end
以下是如何将其合并到库中:
def library_method(&block)
safe_yield(&block)
puts "library_method succeeded"
rescue LocalJumpError
puts "library_method encountered illegal return but resumed execution"
end
以下是行为良好和行为不端用户的用户体验:
def nice_user_method
library_method { 1 + 1 }
end
nice_user_method
# library_method succeeded
def naughty_user_method
library_method { return false if rand > 0.5 }
end
naughty_user_method
# library_method encountered illegal return but resumed execution
评论:
使用raise LocalJumpError
/rescue LocalJumpError
可以解决您在使用毛毯时遇到的问题确保
我选择了LocalJumpError
,因为它似乎是相关的,而且(我想!)没有可能的Ruby代码会导致LocalJumpError
在这种上下文中“自然”被提出。如果结果是错误的,您可以轻松地替换您自己的新异常类。这将打破默认的ruby行为,带来的痛苦大于利润。假设我是使用此技巧的代码的使用者。当我将return
放在我的块中时,我希望它能立即通过控制,我会非常惊讶出现了一些奇怪的异常。用干草覆盖狩猎坑只会隐藏一个陷阱,使代码更难调试。Ruby不是一种保护人们不被射杀的语言,这是它的主要优点。这不是一个好主意,但这是一个好问题。谢谢你提出了一个有趣的问题。你为什么要首先传递(甚至创建)一个包含return
语句的proc?我认为,应该完全允许用户射中自己的脚。