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