Exception handling 戈朗陷入恐慌

Exception handling 戈朗陷入恐慌,exception-handling,error-handling,go,Exception Handling,Error Handling,Go,在下面的代码中,如果没有给出文件参数,则会按照预期为第9行引发恐慌panic:runtime error:index out range 当直接向它传递导致恐慌的内容(os.Args[1])时,我如何“捕捉”这种恐慌并处理它?与PHP中的try/catch或Python中的try/except非常相似 我在这里搜索了StackOverflow,但没有找到任何能回答这个问题的东西 package main import ( "fmt" "os" ) func main() {

在下面的代码中,如果没有给出文件参数,则会按照预期为第9行引发恐慌
panic:runtime error:index out range

当直接向它传递导致恐慌的内容(
os.Args[1]
)时,我如何“捕捉”这种恐慌并处理它?与PHP中的try/catch或Python中的try/except非常相似

我在这里搜索了StackOverflow,但没有找到任何能回答这个问题的东西

package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open(os.Args[1])
    if err != nil {
        fmt.Println("Could not open file")
    }
    fmt.Printf("%s", file)
}

Go不是python,在使用它之前应该正确检查args:

func main() {
    if len(os.Args) != 2 {
         fmt.Printf("usage: %s [filename]\n", os.Args[0])
         os.Exit(1)
    }
    file, err := os.Open(os.Args[1])
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("%s", file)
}

第一:你不会想这么做的。Try-catch样式的错误处理不是错误处理。在Go中,您将首先检查
len(os.Args)
并仅在元素1存在时访问它

对于罕见的情况,您需要抓住恐慌(您的情况是而不是其中之一!),请将
延迟
恢复
结合使用。请参见

一个惊慌失措的程序可以使用内置的
recover()
功能:

recover
函数允许程序管理惊慌失措的goroutine的行为。假设函数
G
延迟调用
recover
的函数
D
,并且在执行
G
的同一goroutine上的函数中发生死机。当延迟函数的运行达到
D
时,
D
调用
recover
的返回值将是传递给
panic
调用的值。如果
D
正常返回,而没有启动新的
panic
,则恐慌序列停止。在这种情况下,在
G
和对
panic
的调用之间调用的函数的状态将被放弃,并恢复正常执行。然后运行
G
D
之前延迟的任何函数,并且
G
的执行通过返回其调用者而终止

如果满足以下任一条件,则recover的返回值为零:

  • panic
    的参数是
    nil
  • 戈罗廷并没有惊慌失措
  • 延迟函数未直接调用
    recover
下面是一个如何使用此功能的示例:

// access buf[i] and return an error if that fails.
func PanicExample(buf []int, i int) (x int, err error) {
    defer func() {
        // recover from panic if one occured. Set err to nil otherwise.
        if (recover() != nil) {
            err = errors.New("array index out of bounds")
        }
    }()

    x = buf[i]
}

请注意,恐慌往往不是正确的解决方案。Go范例是显式地检查错误。一个程序只有在正常程序执行期间没有发生恐慌的情况下才应该恐慌。例如,无法打开文件是可能发生的事情,在内存不足时不应引起恐慌,值得恐慌。然而,这种机制的存在是为了能够捕获这些情况,并且可能会优雅地关闭。

一些Golang官方软件包使用panic/defer+recover作为throw/catch,但仅当它们需要释放一个大的调用堆栈时。在Golang的json包中,使用panic/defer+recover作为throw/catch是最优雅的解决方案

有关panic和recover的真实示例,请参阅Go标准库中的json包。它使用一组递归函数对JSON编码的数据进行解码。当遇到格式不正确的JSON时,解析器调用panic将堆栈展开到顶级函数调用,该函数调用将从panic中恢复并返回适当的错误值(请参阅decode.go中decodeState类型的“error”和“unmarshal”方法)

搜索
d.error(

在您的示例中,“惯用”解决方案是在使用参数之前检查参数,正如其他解决方案所指出的那样

但是,如果你想/需要抓住你能做的任何事情:

package main

import (
    "fmt"
    "os"
)

func main() {

    defer func() { //catch or finally
        if err := recover(); err != nil { //catch
            fmt.Fprintf(os.Stderr, "Exception: %v\n", err)
            os.Exit(1)
        }
    }()

    file, err := os.Open(os.Args[1])
    if err != nil {
        fmt.Println("Could not open file")
    }

    fmt.Printf("%s", file)
}

请注意,在go 1.7之后,对紧急执行错误的恢复处理(例如尝试索引数组越界触发器)可能会更改

见和:

运行时:使执行错误死机值实现
error
接口 使执行恐慌按照命令实现错误,而不是使用字符串进行恐慌

当您恢复紧急错误时,您将能够执行以下操作:

if _, ok := recovered.(runtime.Error); !ok {

这仍在评估中,如所述:

我不知道人们目前在做什么,但从我的观点来看,这已经被打破了很长一段时间,没有人抱怨过,所以他们要么直接依赖于被打破的行为,要么没有人关心。不管怎样,我认为避免做出这种改变是个好主意


我不得不在一个测试案例中陷入恐慌。我被重定向到这里

func.go

func_test.go


使用from(ref from VonC)

我们可以使用recover在不停止进程的情况下管理死机。通过使用defer在任何函数中调用recover,它会将执行返回到调用函数。recover返回两个值,一个是布尔值,另一个是要恢复的接口。使用类型断言,我们可以获得基本错误值 您还可以使用recover打印底层错误

defer func() {
    if r := recover(); r != nil {
        var ok bool
        err, ok = r.(error)
        if !ok {
            err = fmt.Errorf("pkg: %v", r)
        }
    }
}()

请在否决投票前发表评论。你为什么否决我回答OP提出的字面问题?OP明确表示他想从恐慌中恢复过来。我写了一个答案如何做。这是否正确不由我来判断。@FUZxxl:你在推广不良风格。如果有人问:“我想吸一口加芥末的果冻豆。我该怎么做?”?"你会推荐一个解决方案吗?或者给出一个好的建议,说明他的愿望可能是个坏主意,并解释如何使用果冻豆和芥末?@Volker如果他问这个问题,我会给他一个诚实的答案。我可能会在另外的评论中指出,这可能不是最明智的做法。我是谁来评判别人的问题?为了将来的研究Engess(因为没有人提到过):吸入果冻和芥末是一个坏主意。对你所遇到的任何问题考虑不同的方法。只有当你知道你不能继续,只有崩溃时才会惊慌。崩溃可能意味着放弃整个程序,返回一个“内部错误”。来自API或Web请求的消息,或结束其他高级会话
func TestExpectedPanic() {
    got := panicValue(func() { closeTransaction(true) })
    a, ok := got.(error)
    if a != errUnexpectedClose || !ok {
        t.Error("Expected ", errUnexpectedClose.Error())
    }
}

func panicValue(fn func()) (recovered interface{}) {
    defer func() {
        recovered = recover()
    }()
    fn()
    return
}
defer func() {
    if r := recover(); r != nil {
        var ok bool
        err, ok = r.(error)
        if !ok {
            err = fmt.Errorf("pkg: %v", r)
        }
    }
}()