gpt4 book ai didi

ruby - 使用 Ruby `return` 时如何防止 `yield` 出现问题

转载 作者:数据小太阳 更新时间:2023-10-29 06:47:02 25 4
gpt4 key购买 nike

正如每个 Ruby 程序员最终发现的那样,调用包含 return 语句的 block 或过程可能很危险,因为这可能会退出您当前的上下文:

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"

如果您想绝对确定某些代码在您调用 block 或过程后运行,您可以使用begin ... ensure 结构。但是,如果在 yield 期间出现异常,ensure 也会被调用,因此它需要做更多的工作。

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

  1. 使用 safe_yield,检测 yielded block 或 proc 是否使用 return 关键字实际返回。如果是这样,它会引发异常。

    unknown_block = proc do
    return
    end

    ReturnSafeYield.safe_yield(unknown_block)
    # => Raises a UnexpectedReturnException exception
  2. 使用 call_then_yield,您可以调用一个 block ,然后确保执行第二个 block ,即使第一个 block 包含 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,或者是否有任何内置解决方案来防止从我错过的嵌套 block 中快速返回?

最佳答案

有一个内置的解决方案可以检测 block 是否包含 return 语句。

您可以使用 RubyVM::InstructionSequence.disasm对用户传入的 block 进行反汇编,然后在其中搜索throw 1,它代表一个return语句。

这是一个示例实现:

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 可以解决您在使用毯子 ensure 时遇到的问题。

我选择 LocalJumpError 因为它看起来很相关,而且因为(我认为!)没有可能导致 LocalJumpError 在此“自然”引发的 Ruby 代码语境。如果事实证明这是错误的,您可以轻松地替换您自己的新异常类。

关于ruby - 使用 Ruby `return` 时如何防止 `yield` 出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41100983/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com