diff --git a/_examples/catchthrow/main.go b/_examples/catchthrow/main.go new file mode 100644 index 0000000..98e6e1c --- /dev/null +++ b/_examples/catchthrow/main.go @@ -0,0 +1,20 @@ +package main + +import ( + "fmt" + + . "github.com/kevin-cantwell/exceptions" +) + +func main() { + Try(func() { + Try(func() { + panic(int(123)) + }, Catch(func(cause int) { + fmt.Println("caught:", cause) + panic("re-throw") + })) + }, Catch(func(cause string) { + fmt.Println("caught:", cause) + })) +} diff --git a/_examples/nilpanic/main.go b/_examples/nilpanic/main.go index ecbc9f0..6a7fb7e 100644 --- a/_examples/nilpanic/main.go +++ b/_examples/nilpanic/main.go @@ -8,10 +8,11 @@ import ( func main() { Try(func() { - // panic(nil) is undetectable. Just a "feature" of Go. - Throw(nil) - }, CatchNilThrows(func(cause any) { - fmt.Println("nil panic!", cause) + // Typically, nil panics are not detected at runtime. But we can detect them if they + // happen in the Try block. + panic(nil) + }, CatchNil(func(cause any) { + fmt.Printf("nil panic! %T\n", cause) }), Finally(func() { // gets called fmt.Println("This will print after catching the error.") diff --git a/exceptions.go b/exceptions.go index f25cf9c..8ead9f5 100644 --- a/exceptions.go +++ b/exceptions.go @@ -4,6 +4,10 @@ package exceptions // the panic is recovered and its value is passed to the first Catch block that matches // the recovered type. If no suitable Catch is found, it panics with the recovered value. func Try(try func(), thens ...then) { + var ( + // remains true if and only if try panics + panicked bool = true + ) defer func() { defer func() { for _, then := range thens { @@ -12,13 +16,11 @@ func Try(try func(), thens ...then) { } } }() - - // note: nil panics will not be detected unless wrapped with Throw - if cause := recover(); cause != nil { + if panicked { + cause := recover() for _, then := range thens { if then.catch != nil { if caught := then.catch(cause); caught { - caught = true return } } @@ -26,8 +28,8 @@ func Try(try func(), thens ...then) { panic(cause) } }() - try() + panicked = false } type then struct { @@ -48,20 +50,19 @@ func Catch[C any](catch func(C)) then { catch(c) return true } - return false }, } } -func CatchNilThrows(catch func(any)) then { +// CatchNil is a special catch block used for nil panics. +func CatchNil(catch func(any)) then { return then{ catch: func(cause any) bool { - if np, ok := cause.(nilPanic); ok { - catch(np.cause) + if cause == nil { + catch(cause) return true } - return false }, } @@ -78,16 +79,3 @@ func Finally(finally func()) then { finally: finally, } } - -// Throw wraps a call to panic. If the cause itself is nil it will panic with a special type -// that wraps the nil. To detect nil Throws, use CatchNilThrows. -func Throw(cause any) { - if cause == nil { - panic(nilPanic{cause: cause}) - } - panic(cause) -} - -type nilPanic struct { - cause any -}