Ruby中的catch和throw在哪里有用?

我真的没有看到这些的合理使用。 已经有rescueraise ,为什么需要throwcatch ? 似乎它们应该被用来跳出深深的嵌套,但这对我来说只是闻起来像一个goto。 是否有任何良好,干净的用途的例子?

注意: 1.9中的catch / throw看起来有些变化。 这个答案适用于Ruby 1.9。

一个很大的区别是你可以throw任何东西,而不仅仅是从StandardError派生的东西,与raise不同。 像这样愚蠢的东西是合法的,例如:

 throw Customer.new 

但它并不是非常有意义。 但你做不到:

 irb(main):003:0> raise Customer.new TypeError: exception class/object expected from (irb):3:in `raise' from (irb):3 from /usr/local/bin/irb:12:in `
'

它们可以非常有用,可以通过将控制权移出DSL而无需复杂的case / if语句来简化最终用户的DSL

我有一个Ruby应用程序,允许用户通过内部DSL扩展它。 DSL中的某些function需要将控制权返回给我的应用程序的特定部分。 我们举一个简单的例子。 假设用户正在开发一个关于日期的简单扩展

 if today is a holiday then do nothing end week_of_year = today.week.number if week_of_year < 10 then ... 

do nothing会触发一个throw,它将控制权交给exec语句并返回给我。

在某些情况下,我们希望它退出并将控制权交还给我的应用程序,而不是继续执行DSL。 现在,您可以让用户使用大量嵌入式if语句,并自然地结束DSL,但这只是模糊了逻辑试图说的内容。

投掷确实是一个被认为是“危险”的转移,但该死的有时它们是最好的解决方案。

它基本上是一个goto,稍微类似于call / cc,除了控制流是通过名称隐式连接而不是显式地作为参数连接。 throw / catch和raise / rescue之间的区别在于前者旨在用于控制流而不仅仅是特殊情况,并且它不会浪费时间将堆栈跟踪放在一起。

Sinatra对HTTP错误代码使用throw / catch,其中处理程序可以使用throw以结构化方式将控制权交给Sinatra库。 其他类型的HTTP框架使用exception,或者通过返回不同类的响应,但这使得Sinatra(例如)在捕获它之后尝试另一个请求处理程序。

两者之间的区别在于你只能“提高”exception,但可以“抛出”任何东西(1.9)。 除此之外,它们应该是可以互换的,也就是说,应该可以用另一个重写一个,就像@ john-feminella给出的例子一样。