加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

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

发布时间:2020-12-16 22:30:09 所属栏目:百科 来源:网络整理
导读:正如每个 Ruby程序员最终都会发现的那样,调用包含返回语句的块或者进程可能是危险的,因为这可能会退出你当前的上下文: def some_method(_block) puts 1 yield # The following line will never be executed in this example # as the yield is actually a `
正如每个 Ruby程序员最终都会发现的那样,调用包含返回语句的块或者进程可能是危险的,因为这可能会退出你当前的上下文:
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 "1n2n" instead of "1n2n3n"

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

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

>使用safe_yield,检测是否使用return关键字返回生成的块或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

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

解决方法

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

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

这是一个示例实现:

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.如果事实证明是假的,您可以轻松地替换自己的新异常类.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读