Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
当使用Ruby`yield时,如何防止“return`from block”出现问题`_Ruby_Yield - Fatal编程技术网

当使用Ruby`yield时,如何防止“return`from block”出现问题`

当使用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

正如每个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。。。确保构造。但是,由于如果在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?我认为,应该完全允许用户射中自己的脚。