我在使用trap和catch/throw方法时注意到了这种行为,以便更好地理解它们可以/应该如何使用。
在Ruby中,catch和throw方法似乎是成对使用的:
catch(:ctrl_c) do
trap("SIGINT") { throw :ctrl_c }
(1.. ).each {|n| print "."; sleep 0.5 }
end
# SIGINT trapped -> throw called -> catch block exits
# ruby covthrow1p.rb
# => ........
字符串
或者使用提供给throw方法的第二个参数:
def stop_script
puts 'CTRL_C seen'
exit
end
catch(:ctrl_c) do
trap("SIGINT") { throw :ctrl_c, stop_script }
(1.. ).each {|n| print "."; sleep 0.5 }
end
# SIGINT trapped -> throw called -> catch executes stop_script & exits block
# ruby covthrow2p.rb
# => ......CTRL_C seen
型
如果单独使用throw(naked),只提供key参数,则失败:
def stop_script
puts 'CTRL_C seen'
exit
end
trap("SIGINT") { throw :ctrl_c }
(1.. ).each {|n| print "."; sleep 0.5 }
# SIGINT trapped -> throw called -> No catch block -> UncaughtThrowError
# ruby nkdthrow1p.rb
# => .......nkdthrow1p.rb:8:in `throw': uncaught throw :ctrl_c (UncaughtThrowError)
型
但是如果使用裸抛出并提供第二个参数,则会成功!
def stop_script
puts 'CTRL_C seen'
exit
end
trap("SIGINT") { throw :ctrl_c, stop_script }
(1.. ).each {|n| print "."; sleep 0.5 }
# SIGINT trapped -> throw called -> No catch block -> call stop_script ???
# ruby nkdthrow2p.rb
# => ......CTRL_C seen
型
这是预期行为吗?一个执行的工件?一个bug?看起来一般是无害的,但可能会导致混乱的行为。
这些示例是在Windows 10上运行的Ruby 3.2.2上。
1条答案
按热度按时间0kjbasz61#
你对第二个例子的解释是错误的。现在让我们注解掉
exit
,这样我们就可以看到完整的执行过程:字符串
实际发生了什么:
catch
执行其块trap
设置SIGINT
处理程序each
开始执行,并被键盘中断中断SIGINT
处理程序执行throw(:ctrl_c, stop_script)
执行;由于throw
是一个方法,因此需要在方法调用之前计算其参数stop_script
等效于self.stop_script()
,如果存在这样的方法,则执行stop_script
"CTRL_C seen"
,puts
返回nil
exit
,脚本将退出return
,所以stop_script
返回最后一个语句的值,即nil
throw
最终被称为throw(:ctrl_c, nil)
catch
以值nil
退出您的第四个示例是等效的,只是缺少了第一步和最后一步。
至关重要的是,
throw
的第二个参数只是作为返回值传递给catch
的值。它没有“稍后评估”的魔力:它在调用throw
之前求值,就像方法的任何参数一样。如果
throw
在脚本到达其自然结束时没有被catch
捕获,则将打印一个异常。但是,在上一个示例中,假设执行了exit
,脚本随即退出,则不会发生异常情况。