使用Ruby`yield`时如何防止`return`从块中出现问题

前端之家收集整理的这篇文章主要介绍了使用Ruby`yield`时如何防止`return`从块中出现问题前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
正如每个 Ruby程序员最终都会发现的那样,调用包含返回语句的块或者进程可能是危险的,因为这可能会退出你当前的上下文:
  1. def some_method(&_block)
  2. puts 1
  3. yield
  4. # The following line will never be executed in this example
  5. # as the yield is actually a `yield-and-return`.
  6. puts 3
  7. end
  8.  
  9. def test
  10. some_method do
  11. puts 2
  12. return
  13. end
  14. end
  15.  
  16. test
  17.  
  18. # This prints "1\n2\n" instead of "1\n2\n3\n"

如果你想绝对确定你调用了一个块或者proc后有些代码运行,你可以使用begin …确保构造.但是,如果在收益期间有异常,那么确保也会被调用,它需要更多的工作.

我创建了一个tiny module,以两种不同的方式处理这个问题:

>使用safe_yield,检测是否使用return关键字返回生成的块或proc.如果是这样,它会引起一个例外.

  1. unknown_block = proc do
  2. return
  3. end
  4.  
  5. ReturnSafeYield.safe_yield(unknown_block)
  6. # => Raises a UnexpectedReturnException exception

>使用call_then_yield,您可以调用一个块,然后确保执行第二个块,即使第一个块包含return语句.

  1. unknown_block = proc do
  2. return
  3. end
  4. ReturnSafeYield.call_then_yield(unknown_block) do
  5. # => This line is called even though the above block contains a `return`.
  6. end

我正在考虑创建一个快速宝石,还是有内置的解决方案,以防止我错过的嵌套块的快速返回?

解决方法

有一个内置解决方案来检测块是否包含return语句.

您可以使用RubyVM::InstructionSequence.disasm反汇编用户传递的块,然后搜索throw 1代表返回语句.

这是一个示例实现:

  1. def safe_yield(&block)
  2. if RubyVM::InstructionSequence.disasm(block) =~ /^\d+ throw +1$/
  3. raise LocalJumpError
  4. end
  5.  
  6. block.call
  7. end

以下是您将其纳入您的图书馆的方式:

  1. def library_method(&block)
  2. safe_yield(&block)
  3. puts "library_method succeeded"
  4. rescue LocalJumpError
  5. puts "library_method encountered illegal return but resumed execution"
  6. end

而这里的用户体验是一个行为良好和行为不端的用户

  1. def nice_user_method
  2. library_method { 1 + 1 }
  3. end
  4.  
  5. nice_user_method
  6. # library_method succeeded
  7.  
  8. def naughty_user_method
  9. library_method { return false if rand > 0.5 }
  10. end
  11.  
  12. naughty_user_method
  13. # library_method encountered illegal return but resumed execution

评论

使用raise LocalJumpError / rescue LocalJumpError可以解决您在使用毯子时遇到的问题.

我选择了LocalJumpError,因为它似乎是相关的,因为(我想!)没有可能的Ruby代码会导致在这个上下文中“自然”地引发LocalJumpError.如果事实证明是假的,您可以轻松地替换自己的新异常类.

猜你在找的Ruby相关文章