«Go» паникует
Попробую рассказать ещё об одной вещи, которая показалась мне в «Go» странной, дополню предыдущий пост.
Иногда, скомпилированная программа, написанная на «Go» «паникует»:
bolk-osx:Sample bolk$ 6g t.go && 6l t.6 && ./6.out
panic: runtime error: index out of range
runtime.panic+0xac /Users/bolk/go/src/pkg/runtime/proc.c:1083
runtime.panic(0x11ea8, 0xf8400011f0)
runtime.panicstring+0xa3 /Users/bolk/go/src/pkg/runtime/runtime.c:116
runtime.panicstring(0x295e6, 0xce63)
runtime.panicindex+0x25 /Users/bolk/go/src/pkg/runtime/runtime.c:73
runtime.panicindex()
main.main+0x5e /Users/bolk/Проекты/Sample/t.go:6
main.main()
runtime.mainstart+0xf /Users/bolk/go/src/pkg/runtime/amd64/asm.s:77
runtime.mainstart()
runtime.goexit /Users/bolk/go/src/pkg/runtime/proc.c:150
runtime.goexit()
----- goroutine created by -----
_rt0_amd64+0x8e /Users/bolk/go/src/pkg/runtime/amd64/asm.s:64То есть возникает ошибка, которая аварийно завершает выполнение программы. Самый простой способ этого добиться — вызов «panic», более реальный — обратиться за границу массива:
package main
func main() {
arr := []int{1,2,3}
print(arr[1000])
}«Паника», конечно, ситуация исключительная, программист должен постараться не допускать ситуации, в которых она возникает, но всё же, программа «паникует», а нам нужно продолжить её выполнение, например, чтобы произвести
Я спросил пару человек, незнакомых с «Go», что, по их мнению, язык должен бы предоставить программисту для решения этой проблемы, оба человека назвали обработку исключений. К сожалению, тут исключений нет. Подход к решению этой проблемы у «Go»
В «Go» есть поистине замечательная штука — defer, с её помощью можно вызвать функцию когда управление покидает определённую зону видимости. Для языка без деструкторов (а в «Go» их нет) это очень удобно — можно закрывать файлы, сокеты и прочее в таком полуавтоматическом режиме — важно не забыть поставить нужный вызов только один раз.
Функции с defer будут вызваны когда управление покидает выбранную нами зону по любой причине, в том числе, когда произошла «паника». В функции можно проверить, была ли она вызвана нормальным ходом событий
Выглядит это вот так:
package main
func main() {
defer func() {
if r := recover(); r != nil {
print("Recovered")
}
}()
arr := []int{1,2,3}
print(arr[1000])
}Происходит тут следующее: в зоне вызова функции «main» (тут, как в Си, при запуске вызывается «main») определена анонимная функция, которая тут же вызвана как defer, при возникновении ошибки (обращение за границу массива в предпоследней строке), управление покидает зону видимости функции «main»
Комментарии 6
package main
import «big»
func main() {
a := big.NewInt(0)
b := big.NewInt(0)
print(a.Quo(a, b))
}
Метод Quo описан в документации: http://golang.org/pkg/big/#Int.Quo
Не понял мысли. Зачем тут падать? Ну вышли,
except: print('Recover')»
Recover
ну в питоне — это ж исключение, не?
есть места, где есть и исключения, и паники. исключения ловятся и используются в ситуациях: не хватило памяти, не смогли открыть файл, не удалось соединиться с удалённым хостом и т.д. паники приводят сразу к закрытию приложения. вызываться они могут в том числе и при выходе за границы массивы, при копировании строк, когда в строке назначении места не хватает, при доставании со стека больше, чем положили и т.д.
ведь обращение за пределы массива однозначно свидетельствует о логической ошибке в программе. зачем ей вообще работать с
Комментарий для zg.livejournal.com:
Исключение. Исключение в Пайтоне это нечто-то исключительное, там оно сплошь и рядом, рекомендуется использовать их как можно более широко. Например, у строки есть метод, который порождает исключение, если подстрока не найдена.
Вот я как бы и намекаю на то, чтоиз-за того, что в языке нет исключений, используется паника. Взглянем, например, на внутренности модуля regexp ( http://golang.org/src/pkg/regexp/regexp.go#L618 ):
re-panic if error was not an Error, e.g. nil-pointer exception
что-то рассказывать об ошибке.
618 func Compile(str string) (regexp *Regexp, error os.Error) {
619 regexp = new(Regexp)
620 // doParse will panic if there is a parse error.
621 defer func() {
622 if e := recover(); e != nil {
623 regexp = nil
624 error = e.(Error) // Will
625 }
626 }()
627 regexp.expr = str
628 regexp.inst = make([]*instr, 0, 10)
629 regexp.doParse()
630 return
631 }
Легко заметить, что тут «паника» используется ровно как исключение. Даже видно, что «паника» «пробрасывается» выше, если это не та ошибка, которую мы стремились подавить.
Причём «паника» она, как исключения, типизирована. Например: panic(42), panic(«Hello»), можно передавать структуры. То есть можно по типу и содержимому может
В Пайтоне я бы сделалкак-то так:
слишком-то удобно.
try:
cached = cache[somekey]
except IndexError:
cached = None
Где тут логическая ошибка?
Или вот в моём примере про деление на ноль (вызов Quo), это такая страшная ситуация, что программа должна валиться и выходить?
Вообще, когда я читал документацию, у меня тоже сложилось впечатление, что «паника» — это unrecoverable fatal, теперь, когда я побольше почитал код модулей, я начинаю понимать, что «паника» это аналог исключений и относиться к ней нужно соответственно. Но обрабатывать её («панику») не