为了账号安全,请及时绑定邮箱和手机立即绑定

Google的“ Go”语言多值返回语句是异常的替代方法吗?

Google的“ Go”语言多值返回语句是异常的替代方法吗?

Go
蛊毒传说 2021-04-14 18:15:43
在我看来,Google对例外的替代方法是GO:多值返回“ return val,err;”GO,C ++:无检查(早期返回)GO,C ++:“处理该死的错误”(我的说法)C ++:断言(表达式)GO:延迟/紧急/恢复是询问此问题后添加的语言功能多值回报是否足够有用以充当替代选择?为什么将“资产”视为替代品?如果发生程序错误而无法正确处理的程序而导致程序停止运行,那么Google认为可以吗?有效的GO:多个返回值Go的不寻常功能之一是函数和方法可以返回多个值。这可以用来改善C程序中的一些笨拙的习惯用法:带内错误返回(例如,对于EOF为-1)和修改自变量。在C语言中,写入错误由负数表示,错误代码被隐藏在易失性位置中。在Go中,Write可以返回一个计数和一个错误:“是的,您写了一些字节,但不是全部,因为您填满了设备”。软件包os中* File.Write的签名为:func (file *File) Write(b []byte) (n int, err Error)如文档所述,当n!= len(b)时,它返回写入的字节数和非nil错误。这是一种常见的样式。有关更多示例,请参见错误处理部分。有效的GO:命名结果参数可以给Go函数的返回或结果“参数”指定名称,并将其用作常规变量,就像传入的参数一样。命名后,函数开始时会将它们初始化为其类型的零值;否则,将它们初始化为零。如果函数执行不带参数的return语句,则将结果参数的当前值用作返回值。名称不是强制性的,但它们可以使代码更短,更清晰:它们是文档。如果我们命名nextInt的结果,则显而易见,返回的int是哪个。func nextInt(b []byte, pos int) (value, nextPos int) {由于命名结果已初始化并绑定到未经修饰的返回值,因此它们既可以简化又可以澄清。这是io.ReadFull的一个很好使用它们的版本:func ReadFull(r Reader, buf []byte) (n int, err os.Error) {  for len(buf) > 0 && err == nil {    var nr int;    nr, err = r.Read(buf);    n += nr;    buf = buf[nr:len(buf)];  }  return;}Panic是一个内置函数,可停止常规控制流并开始发出恐慌。当函数F调用紧急情况时,F的执行停止,F中任何延迟的函数都将正常执行,然后F返回其调用方。对于呼叫者,F然后表现得像是发生了恐慌。该过程将继续执行堆栈,直到返回当前goroutine中的所有函数为止,此时程序崩溃。紧急事件可以通过直接调用紧急事件来启动。它们也可能是由运行时错误引起的,例如越界数组访问。恢复是一个内置功能,可以重新获得对紧急恐慌例程的控制。恢复仅在延迟函数内部有用。在正常执行期间,恢复调用将返回nil并且没有其他效果。如果当前goroutine处于恐慌状态,则调用recovery会捕获为panic提供的值并恢复正常执行。这是一个示例程序,演示了恐慌和延迟的机制:<snip>有关恐慌和恢复的真实示例,请参阅Go标准库中的json包。它使用一组递归函数对JSON编码的数据进行解码。遇到格式错误的JSON时,解析器调用panic会将堆栈退回到顶层函数调用,该函数从panic中恢复并返回适当的错误值(请参见decode.go中的“ error”和“ unmarshal”函数) 。regexp包的Compile例程中有此技术的类似示例。Go库中的约定是,即使程序包内部使用了紧急情况,其外部API仍会显示显式的错误返回值。defer的其他用法(在先前给出的file.Close()示例之外)包括释放互斥锁:mu.Lock()   defer mu.Unlock
查看完整描述

3 回答

?
SMILET

TA贡献1796条经验 获得超4个赞

多次返回不是Go唯一的,并且不能替代异常。用C(或C ++)术语来说,它们是返回包含多个值的结构(对象)的简洁且用户友好的替代方法。

如果您仅此意思,它们确实提供了指示错误的便捷方法。

为什么将“资产”视为替代品?

断言最初用于调试。他们在程序处于“不可能”状态的情况下暂停了该程序,设计认为这种情况不应该发生,但是无论如何还是会发生。返回错误不太可能有太大帮助。代码库显然还无法正常工作,那么到底如何才能成功恢复呢?当有一个需要引起注意的错误时,您为什么还要这么做?

在生产代码中使用断言有些不同-显然存在性能和代码大小方面的顾虑,因此通常的方法是在代码分析和测试确信您“不可能”的情况确实不可能时,将它们删除。但是,如果您正在以这种偏执级别运行代码,它正在对自身进行审核,那么您还可能会偏执于:如果让代码继续在“不可能”状态下运行,那么它可能会做一些危险的事情:破坏有价值的数据,从而超出了堆栈分配的范围,并可能创建安全漏洞。同样,您只想尽快关闭。

您断言的内容确实与您使用异常的内容不同:当C ++和Java之类的编程语言为“不可能”的情况(logic_errorArrayOutOfBoundsException)提供异常时,它们无意间鼓励一些程序员认为他们的程序应该尝试从真正无法控制的情况中恢复过来。有时这是适当的,但是出于充分的原因,这里有Java建议不要捕获RuntimeExceptions。偶尔捕获一个是一个好主意,这就是它们存在的原因。捕获它们几乎总是一个好主意,这意味着它们无论如何都等于暂停了程序(或至少是线程)。


查看完整回答
反对 回复 2021-04-26
?
哔哔one

TA贡献1854条经验 获得超8个赞

您应该阅读几篇有关异常的文章,以了解返回值不是异常。不能采用“带内” C方式或其他任何方式。


在不深入讨论的情况下,应该在发现错误条件的地方抛出异常,并在可以有意义地处理错误条件的地方捕获异常。返回值仅在层次结构堆栈中的第一个函数中处理,该函数可能会或不会如何处理问题。一个简单的示例是一个配置文件,该文件可以将值检索为字符串,并且还支持处理成类型的return语句:


class config {

   // throws key_not_found

   string get( string const & key );

   template <typename T> T get_as( string const & key ) {

      return boost::lexical_cast<T>( get(key) );

   }

};

现在的问题是,如果找不到密钥,您将如何处理。如果您使用返回码(例如,在返回过程中),则问题在于get_as必须处理错误码get并采取相应措施。由于它并不真正知道该怎么办,因此唯一明智的做法是手动向上游传播错误:


class config2 {

   pair<string,bool> get( string const & key );

   template <typename T> pair<T,bool> get_as( string const & key ) {

      pair<string,bool> res = get(key);

      if ( !res.second ) {

          try {

             T tmp = boost::lexical_cast<T>(res.first);

          } catch ( boost::bad_lexical_cast const & ) {

             return make_pair( T(), false ); // not convertible

          }

          return make_pair( boost::lexical_cast<T>(res.first), true );

      } else {

          return make_pair( T(), false ); // error condition

      }

   }

}

该类的实现者必须添加额外的代码来转发错误,并且该代码与问题的实际逻辑混杂在一起。在C ++中,这可能比为多种赋值设计的语言(a,b=4,5)更为繁重,但是,如果逻辑取决于可能的错误(此处lexical_cast仅在有实际字符串的情况下执行调用),则必须缓存值变成变量。




查看完整回答
反对 回复 2021-04-26
?
慕桂英4014372

TA贡献1871条经验 获得超13个赞

它不是Go语言,但在Lua中,多次返回是处理异常的极为常见的习惯用法。


如果您有类似的功能


function divide(top,bottom)

   if bottom == 0 then 

        error("cannot divide by zero")

   else

        return top/bottom

   end

end

然后,当bottom值为0时,将引发异常,并且程序的执行将停止,除非您将函数包装divide在pcall(或受保护的调用)中。


pcall 总是返回两个值:第一个是result是一个布尔值,指示该函数是否成功返回,第二个结果是返回值或错误消息。


以下(伪造的)Lua代码片段显示了此用法:


local top, bottom = get_numbers_from_user()

local status, retval = pcall(divide, top, bottom)

if not status then

    show_message(retval)

else

    show_message(top .. " divided by " .. bottom .. " is " .. retval)

end

当然pcall,如果您要调用的函数已经以的形式返回,则不必使用status, value_or_error。


多次回报对于Lua来说已经足够好了,所以虽然不能确保它对Go足够好,但是它支持了这个想法。


查看完整回答
反对 回复 2021-04-26
  • 3 回答
  • 0 关注
  • 218 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信