【问题标题】:Perl - best practices in dealing with invalid data passed to a subPerl - 处理传递给子的无效数据的最佳实践
【发布时间】:2017-07-28 19:42:09
【问题描述】:

当数据被错误地传递给子例程时,Perl 中的最佳实践是什么?潜艇应该死掉还是返回?

这是我通常做的事情

my @text = ('line 1', 'line 2');

print_text(\@text) 
   or die "ERROR: something went wrong in the sub";

sub print_text{
   my ($aref_text) = @_;
   return unless ref($aref_text) eq "ARRAY";
   print "$_\n" for @{$aref_text};
   return 1;
}

如果传递的输入无效并且它希望调用者像这里一样检查错误,那么这里 sub 只会返回。我想知道在子级别“死”是否总是更好的做法。在大脚本中,我害怕这样做,因为我不想仅仅因为一些简单的子失败而杀死整个脚本。

另一方面,我害怕只是返回,因为如果调用者忘记检查 sub 是否返回 true,那么脚本将继续运行,并且可能会发生奇怪的事情。

谢谢

【问题讨论】:

  • @hanshenrik:我看不出这有什么帮助。
  • 这应该是调试的一部分,我认为编写这样的特定检查代码会给人一种错误的安全感。确保给定的参数确实是一个数组引用,例如,一个数组引用只涵盖了一小部分可能的错误,如果您尝试将其他东西作为数组取消引用,Perl 将引发它自己的异常。到目前为止,最常见的错误在很大程度上是无法检测到的,例如您的引用可能引用了一个包含旧数据的数组。
  • 很少有代码在不使整个运行无效的情况下失败,因此最好向die 提供有用的信息,而不是让您的程序继续运行并提供不正确的结果。但是,再多的类型检查也无法提供与最小测试程序相同的保护。这就是为什么 Java 和 C++ 的复杂而冗长的语法是基于一种误解。 Perl 在很多方面都臭名昭著,但容易出错的代码并不是其中之一,即使它只有非常基本的类型检查。
  • 请注意,如果返回 0,则返回 or 的简单决定将变为 die。我认为你的意思只是作为一个例子,但是如果0(或空字符串)一个有效的回报呢?然后你会以defined 为条件,所以我想是func() // die ...。但是,如果返回 undef 是解决无效结果的好方法怎么办?等等。这是一个更大的设计决策的一部分,原则上应该考虑到你的所有代码。没有“最佳实践”。
  • 真正重要的是有单元测试来测试你在坏变量上的边缘情况,这样你就可以确信如果用户确实发送了不正确的数据,你就会确切地知道程序在这些情况下会做什么。

标签: perl


【解决方案1】:

这完全属于如何处理子程序中的错误的问题。

原则上,这些是处理子程序错误的方法

  • 返回代码,其中一些表示错误

  • 返回“特殊”值,例如 Perl 中的 undef

  • 抛出异常,Perl 中的设备是die

调用者要么检查返回,要么测试undef,或者使用eval 来捕获和处理die。什么最合适完全取决于上下文和代码的作用。

我认为现代语言没有太多理由被限制为指示错误的“代码”(如负值)。一方面,要么干扰合法返回,要么限制它们通过指针/引用进行,这是一个重大的设计决策。

返回undef 通常是一种很好的折中方法,尤其是在不太复杂的代码中。它表明子系统的某些“失败”无法执行其应做的事情。但是,即使在最小的 subs 中,undef 也可能适合指示不可接受的结果。那么如果它也被用于错误的输入,我们就会有区分这些失败的问题。

基于简单的die 在Perl 中引发异常,增加了更多可能性。在复杂的代码中,您可能希望编写(或使用)一个错误处理类,该类模仿具有它的语言的异常处理支持,然后抛出它。

my $error_obj = ErrorHandlingClass->new( params );

... or die $error_obj;

然后调用代码可以分析对象。这将是最结构化的方式。

Path::Tiny 是一个不错且简单的示例,在其 source 中可以找到自己的 Path::Tiny::Error

同样,适合任何特定情况的方法取决于该应用程序的详细信息。


一些关于直接问题的问题。

die 中的无信息消息强调了返回什么的困境(它没有告诉我们失败的原因)。但是在这种情况下,我们如何使失败信息丰富呢?

请注意,如果 sub 返回 0 或空字符串,您的 or 会生成 die。如果我们将其替换为//(定义或),那么对于undef 上的die,如果undef 也可能指示错误结果,我们仍然无法打印特定消息。

因此,在这种情况下,您可能希望函数在输入错误时使用die,并带有合适的消息。

这将在出现问题后进行调试。如果代码需要能够恢复,那么您最好返回更多结构化信息——抛出(或返回)您要编写的错误处理类的对象。 (作为临时的权宜之计,您可以解析来自die 的消息。)

关于检查退货的古老纪律问题,die 是一个很好的工具。没有“simple sub”是不值得的——你不想继续出错,所以可以die。而在复杂的项目中,错误处理更加复杂,因此我们需要更多工具和结构,而不是更少。

回想一下,异常“冒泡”,如果未处理,则向上传播调用堆栈,die 也是如此。这可以很好地用于调试,而无需在每次调用时都使用eval。最后,大部分都是调试的一部分。

对此没有“最佳实践”。但是默认die-ing 还是比较合理的。

【讨论】:

  • die 真的可以被抓到和处理吗?你能做类似 try{die 'foo';}catch($ex){} 的事情吗?
  • @hanshenrik 哦,是的,这就是 Perl 中(简单的)异常处理机制的原因。它被eval(它的块形式)捕获。有一些模块封装了它并提供try 风格的语法,还有一些远远超出的模块。但这一切都基于die-eval
  • @JohnnyLoo 我在你的问题上添加了一个更直接的 cmets 部分
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-30
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多