【问题标题】:What's the best practice in case something goes wrong in Perl code? [duplicate]如果 Perl 代码出现问题,最佳实践是什么? [复制]
【发布时间】:2026-02-04 00:30:01
【问题描述】:

可能的重复:
How can I cleanly handle error checking in Perl?
What’s broken about exceptions in Perl?

我看到了这样的代码:

do_something($param) || warn "something went wrong\n";

我还看到了这样的代码:

eval {
  do_something_else($param);
};
if($@) {
  warn "something went wrong\n";
}

我应该在我的所有子程序中使用 eval/die 吗?我应该根据从子例程返回的内容编写所有代码吗? eval'ing 代码(一遍又一遍)不会让我慢下来吗?

【问题讨论】:

  • 假装别人写的? ;)
  • 是的:)。当它说作者:Geo时有点难。 :)
  • 有关 perl 中异常的广泛讨论,请参阅*.com/questions/2165161/…
  • 为服务器机架使用坚固的钢框架笼,这样 CPU 爆炸产生的爆炸不会炸毁整个街区。

标签: perl exception-handling


【解决方案1】:

eval 不是字符串eval,所以不,它并不慢。绝对推荐使用它。

虽然它的工作方式有一些令人讨厌的微妙之处(主要是因为$@ 是一个全局变量这一事实的令人讨厌的副作用),所以请考虑使用Try::Tiny 而不是记住所有你知道的小技巧需要使用eval进行防御。

【讨论】:

    【解决方案2】:
    do_something($param) || warn "something went wrong\n";
    

    在这种情况下,如果出现问题,do_something 应返回错误代码。要么它不能死,要么它死了,这是一个非常不寻常的情况。

    eval {
      do_something_else($param);
    };
    if($@) {
      warn "something went wrong\n";
    }
    

    这里假设do_something_else 传达出错信息的唯一机制是抛出异常。

    如果do_something_else在真正异常的情况下抛出异常,而在其他一些情况下返回错误值,你也应该检查它的返回值。

    使用eval 的块形式不会在运行时导致额外的编译,因此没有严重的性能缺陷:

    在第二种形式中,BLOCK 中的代码只被解析一次——同时围绕eval 本身的代码被解析——并在当前 Perl 程序的上下文中执行。这种形式通常用于比第一种更有效地捕获异常(见下文),同时还提供了在编译时检查 BLOCK 中的代码的好处。

    【讨论】:

      【解决方案3】:

      warn 的模块非常烦人。要么成功,要么失败。不要在终端上打印一些东西然后继续运行;我的程序无法根据您打印的某些消息采取行动。如果程序可以继续运行,则仅在明确告知您可以的情况下打印一条消息。如果程序无法继续运行,die。这就是它的用途。

      当出现问题时总是抛出异常。如果你能解决问题,那就解决它。如果您不能解决问题,请不要尝试;只需抛出异常并让调用者处理它。 (如果您无法处理您调用的异常,请不要处理。)

      基本上,许多程序存在错误的原因是它们试图修复无法修复的错误。在出现问题的第一个迹象时干净地死掉的程序很容易调试和修复。一个在混乱时继续运行的程序只会破坏数据并惹恼所有人。所以不要那样做。尽快死去。

      【讨论】:

        【解决方案4】:

        你的两个例子做了完全不同的事情。第一个检查错误的返回值,并采取一些措施作为响应。第二个检查被调用代码的实际死亡

        您必须自己决定在每种情况下哪种操作是合适的。我建议在大多数情况下简单地返回 false 。如果您遇到严重到无法继续的错误(或者继续没有意义,但即使那样您仍然可以返回 false),您应该明确地dieing。

        eval {} 中包装一个块与在eval "" 中包装任意代码不同。在前一种情况下,代码仍然在编译时解析,并且不会产生任何额外的开销。您将简单地捕获该代码的任何死亡(但您不会有任何迹象表明出了什么问题或您的代码走了多远,除了$@ 中留给您的值)。在后一种情况下,代码被 Perl 解释器视为一个简单的字符串,直到它被实际评估,因此在调用解释器时这里有一定的成本(并且你失去了对代码的所有编译时检查)。

        顺便说一句,您调用eval 并检查$@ 值的方式不是推荐的形式;有关 Perl 中异常陷阱和技术的广泛讨论,请参阅 this discussion

        【讨论】:

        • 当我返回false时,我应该如何同时提供错误消息?
        • @Geo:您可以使用warn() 将字符串输出到stderr,或者使用日志实用程序(例如Log::Log4perl)记录消息。
        【解决方案5】:

        第一个版本非常“失败”,而且很容易理解。这个成语的唯一缺点是它只能在短的情况下阅读。如果错误处理需要更多逻辑,请使用第二个版本。

        【讨论】:

          【解决方案6】:

          目前还没有人真正解决“最佳实践”部分,所以我会加入。

          是的,当出现问题时,您应该绝对在代码中抛出异常,并且应该尽早进行(这样您就可以限制需要调试的代码以找出问题所在)导致它)。

          执行诸如 return undef 之类的代码来表示失败并不是特别可靠,仅仅是因为人们倾向于使用它而不检查 undef 返回值——这意味着他们假设的变量实际上可能没有有意义的东西.这会导致复杂的、难以调试的问题,甚至会在稍后在以前工作的代码中出现意想不到的问题。

          更可靠的方法是编写您的代码,以便在出现问题时它会死掉,然后仅当您需要从该故障中恢复时,将对它的任何调用包装在 eval{ .. } (或者,更好的是,try { .. } catch { .. } 来自Try::Tiny,如前所述)。在大多数情况下,调用代码无法恢复任何有意义的事情,因此在常见情况下调用代码仍然很简单,您可以假设您会得到一个有用的值。如果确实出现问题,那么您将从失败的代码的实际部分收到错误消息,而不是默默地获得 undef。如果您的调用代码可以做一些事情来恢复故障,那么它可以安排捕获异常并做任何它需要做的事情。

          值得一读的是Exception classes,这是一种向调用代码发送额外信息的结构化方式,并允许它选择想要捕获和无法处理的异常。您可能不想在代码中的任何地方都使用它们,但是当您遇到一些复杂的东西可能以同样复杂的方式失败时,它们是一种有用的技术,并且您希望安排可恢复的失败。

          【讨论】: