Подтвердить что ты не робот

Ловля паники в Голанге

При использовании следующего кода, если аргумент файла не указан, для строки 9 panic: runtime error: index out of range выбрана паника.

Как я могу "поймать" эту панику и обработать ее, когда прямо передавая ей что-то (os.Args[1]), вызывающее панику? Очень похоже на try/catch в PHP или try/except в Python.

У меня был поиск здесь в 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)
}
4b9b3361

Ответ 1

Go не является питоном, вы должны правильно проверить аргументы перед его использованием:

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)
}

Ответ 2

Программа паники может восстановить с помощью встроенной функции recover():

Функция recover позволяет программе управлять поведением паникой goroutine. Предположим, что функция G отбрасывает функцию D, которая вызывает recover, и возникает паника в функции на том же языке, в котором выполняется G. Когда выполнение отложенных функций достигает D, возвращаемое значение D вызывает recover будет значением, переданным вызову panic. Если D возвращается нормально, не запустив новый panic, приостанавливающая последовательность останавливается. В этом случае состояние функций, вызываемых между G и вызовом panic, отбрасывается, и нормальное выполнение возобновляется. Любые функции, отложенные на G до D, затем выполняются и выполнение G завершается, возвращая его вызывающему.

Возвращаемое значение восстановления равно нулю, если выполнено одно из следующих условий:

  • 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 является проверка ошибок в явном виде. Программа должна только паниковать, если обстоятельства, при которых она паникует, не происходят во время обычной программы. Например, неспособность открыть файл - это то, что может произойти и не должно вызывать панику при нехватке памяти, стоит паники. Тем не менее, этот механизм существует, чтобы иметь возможность поймать даже эти случаи и, возможно, закрыть его изящно.

Ответ 3

Во-первых: Вы не хотели бы этого делать. Обработка ошибок в стиле Try-catch не является обработкой ошибок. В Go вы проверите len(os.Args) первым и получите доступ к элементу 1 только в случае присутствия.

В редких случаях вам нужно поймать панику (и ваш случай не один из них!) используйте defer в комбинации с recover. См. http://golang.org/doc/effective_go.html#recover

Ответ 4

Некоторые официальные пакеты Golang используют panic/defer + restore как throw/catch, но только когда им нужно развернуть большой стек вызовов. В пакете Golang json использование panic/defer + restore, как throw/catch, является наиболее элегантным решением.

с http://blog.golang.org/defer-panic-and-recover

Для реального примера паники и восстановления см. Пакет json из стандартной библиотеки Go. Он декодирует JSON-кодированные данные с помощью набора рекурсивных функций. При обнаружении искаженного JSON синтаксический анализатор вызывает панику, чтобы размотать стек для вызова функции верхнего уровня, который восстанавливается после паники и возвращает соответствующее значение ошибки (см. Методы error и unmarshal типа decodeState в decode.идти).

Поиск d.error( адресу http://golang.org/src/encoding/json/decode.go

В вашем примере "идиоматическое" решение заключается в проверке параметров перед их использованием, как указали другие решения.

Но, если вы хотите/должны поймать все, что вы можете сделать:

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)
}

Ответ 5

Мы можем управлять паникой, не останавливая процесс, используя восстановление. Вызывая recovery в любой функции, использующей defer, она возвращает выполнение вызывающей функции. Recover возвращает два значения, одно из которых является логическим, а другое - интерфейсом для восстановления. Используя утверждение типа, мы можем получить лежащее в основе значение ошибки. Вы также можете распечатать лежащую в основе ошибку, используя recovery.

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

Ответ 6

Мне пришлось ловить панику в тестовом примере. Я перенаправлен сюда.

func.go

var errUnexpectedClose = errors.New("Unexpected Close")
func closeTransaction(a bool) {
    if a == true {
        panic(errUnexpectedClose)
    }
}

func_test.go

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
}

Используется https://github.com/golang/go/commit/e4f1d9cf2e948eb0f0bb91d7c253ab61dfff3a59 (ссылка из VonC)

Ответ 7

Обратите внимание, что обработка восстановления паники Ошибка выполнения (например, попытка индексирования триггера из-за границы) может измениться с шагом 1.7 после issue 14965

Смотрите CL 21214 и его тест:

runtime: сделать ошибки при запуске панических значений реализовать интерфейс Error

Сделать паники исполнения реализована ошибка, как указано Паники (specs) времени выполнения, а не паники со строками.

При восстановлении панической ошибки вы сможете:

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

Это все еще оценивается и Дейв Чейни. отмечает:

Я не знаю, что делают люди в настоящее время, но из моего POV это было сломано в течение долгого времени, и никто не жаловался, поэтому они либо явно полагаются на нарушенное поведение, либо никто не заботится. В любом случае, я думаю, что это хорошая идея, чтобы избежать этого изменения.