By George Aristy / llorllale
panic()
: built-in function that starts panickingnil
pointer dereference
func main() {
var s *string
_ = *s // nil pointer dereference; program exits immediately
fmt.Println("Go is awesome!")
}
Output:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4553e2]
goroutine 1 [running]:
main.main()
/home/llorllale/dev/llorllale/demo/talks/golang-panics/go/main.go:5 +0x2
panic()
func main() {
panic("oh no!") // invoking panic(); program exits immediately
fmt.Println("Go is awesome!")
}
Output:
panic: oh no!
goroutine 1 [running]:
main.main()
/home/llorllale/dev/llorllale/demo/talks/golang-panics/go/main.go:11 +0x27
public class Main {
public static void main(String... args) {
throw new RuntimeException("oh no!"); // program exits immediately
System.out.println("Java is awesome!");
}
}
Output:
Exception in thread "main" java.lang.RuntimeException: oh no!
at Main.main(Main.java:6)
public class Main {
public static void main(String... args) {
try {
throw new RuntimeException("oh no!");
} catch(RuntimeException e) {
System.out.println("phew! Managed to recover.");
}
System.out.println("Java is awesome!");
}
}
Output:
phew! Managed to recover.
Java is awesome!
recover()
is a built-in function that regains control of a panicking goroutine.
func main() {
panic("oh no!")
recover()
fmt.Println("Go is awesome!")
}
Output (not what we want):
panic: oh no!
goroutine 1 [running]:
main.main()
/home/llorllale/dev/llorllale/demo/talks/golang-panics/go/main.go:19 +0x27
recover()
work?defer
by example
func main() {
defer fmt.Println("deferred function!")
fmt.Println("Go is awesome!")
}
Output:
Go is awesome!
deferred function!
func main() {
defer func() { // defer recover()
if err := recover(); err != nil {
fmt.Printf("err: %s\n", err) // handle panic
fmt.Println("phew! Managed to recover.")
}
}()
panic("oh no!") // code that panics
}
Output:
err: oh no!
phew! Managed to recover.
func main() {
foo()
}
func foo() {
bar()
}
func bar() {
funcThatPanics()
}
func funcThatPanics() {
panic("oh no!")
}
Output:
panic: oh no!
goroutine 1 [running]:
main.funcThatPanics(...)
/home/llorllale/dev/llorllale/demo/talks/golang-panics/go/main.go:52
main.bar(...)
/home/llorllale/dev/llorllale/demo/talks/golang-panics/go/main.go:48
main.foo(...)
/home/llorllale/dev/llorllale/demo/talks/golang-panics/go/main.go:44
main.main()
/home/llorllale/dev/llorllale/demo/talks/golang-panics/go/main.go:40 +0x29
func main() {
fmt.Println("entering main")
foo()
fmt.Println("exit main")
}
func foo() {
fmt.Println("entering foo")
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
fmt.Println("phew! Managed to recover.")
}
}()
bar()
fmt.Println("exit foo")
}
func bar() {
fmt.Println("entering bar")
funcThatPanics()
fmt.Println("exit bar")
}
func funcThatPanics() {
fmt.Println("entering funcThatPanics")
panic("oh no!")
fmt.Println("exit funcThatPanics")
}
Output:
entering main
entering foo
entering bar
entering funcThatPanics
oh no!
phew! Managed to recover.
exit main
func main() {
defer func() {
if err := recover(); err != nil { // ineffective
fmt.Println("phew! Managed to recover.")
}
}()
fmt.Println("enter main")
done := make(chan struct{})
go func() {
funcThatPanics() // unhandled panic in goroutine
}()
<-done
fmt.Println("exit main")
}
func funcThatPanics() {
fmt.Println("enter funcThatPanics")
panic("oh no!")
fmt.Println("exit funcThatPanics")
}
enter main
enter funcThatPanics
panic: oh no!
goroutine 7 [running]:
main.funcThatPanics()
/home/llorllale/dev/llorllale/demo/talks/golang-panics/go/main.go:95 +0x65
main.main.func2()
/home/llorllale/dev/llorllale/demo/talks/golang-panics/go/main.go:85 +0x17
created by main.main
/home/llorllale/dev/llorllale/demo/talks/golang-panics/go/main.go:84 +0x86
public class Main {
public static void main(String... args) throws InterruptedException {
System.out.println("enter main");
var done = new ArrayBlockingQueue(1);
new Thread(Main::funcThatErrors).start();
done.take(); // program hangs here
System.out.println("exit main");
}
private static void funcThatErrors() {
throw new Error("oh no!");
}
}
enter main
Exception in thread "Thread-0" java.lang.Error: oh no!
at Main.funcThatErrors(Main.java:39)
at java.base/java.lang.Thread.run(Thread.java:833)
func main() {
type aThing struct {
This string
That int
}
defer func() {
if err := recover(); err != nil {
switch err.(type) {
case aThing:
fmt.Printf("I caught a thing: %+v\n", err)
default:
fmt.Println("oops - I don't know what I caught")
}
}
}()
panic(aThing{
This: "this is a thing!",
That: 42,
})
}
I caught a thing: {This:this is a thing! That:42}
func doSomething() (err error) {
defer func() {
err = recover()
}()
doStep1()
doStep2()
doStep3()
doStep4()
doStep5()
return
}
func doStepN() {
...
if err != nil {
panic(err)
}
...
if done {
panic(nil)
}
}
func main() {
n := func () (result int) {
defer func() {
if v := recover(); v != nil {
if n, ok := v.(int); ok {
result = n
}
}
}()
func () {
func () {
func () {
// ...
panic(123) // panic on succeeded
}()
// ...
}()
}()
// ...
return 0
}()
fmt.Println(n) // 123
}
func main() {
done := make(chan struct{})
for range []int{1, 2, 3} {
go func() {
defer func() {
if err := recover(); err != nil {
fmt.Println("phew! Managed to recover.")
}
}()
funcThatMayPanic()
}()
}
<-done
}
func funcThatMayPanic() {
panic("oh no!")
}