Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions internal/exercises/catalog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -185,3 +185,16 @@ projects:
- "Use Go's `time.Unix()` to convert an epoch to time."
- "Use `t.Unix()` to convert time back to epoch."
- "Remember Go’s `time.Parse` can help parse date strings."

- slug: 37_recover
title: Safe Panic Recovery
difficulty: medium
topics: ["panic", "recover", "defer"]
hints:
- hint_type: concept
text: The `recover()` function is ONLY useful inside a function that is executed by a `defer` statement. Outside of a deferred function, `recover` will return `nil` and have no effect.
- hint_type: structure
text: You must place a `defer` statement at the beginning of the `Run` function. The function passed to `defer` is where you should call `recover()`.
- hint_type: solution
text: "The core logic of the deferred function should look like this: `if r := recover(); r != nil { recoveredValue = r }`"

30 changes: 30 additions & 0 deletions internal/exercises/solutions/37_recover/recover.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package recover_exercise

// DoWork simulates a function that might panic if the input is negative.
func DoWork(n int) {
if n < 0 {
// A panic occurs if n is negative
panic("input cannot be negative")
}
// Normal, non-panicking code path...
}

// Run calls DoWork and uses defer/recover to safely handle any panic.
// It returns the recovered panic value or nil if no panic occurred.
func Run(n int) (recoveredValue interface{}) {
// The deferred function is executed just before Run returns.
defer func() {
// Call recover() to check if a panic has occurred.
if r := recover(); r != nil {
// If recover() returns a non-nil value (a panic occurred),
// assign it to the named return variable 'recoveredValue'.
recoveredValue = r
}
}()

DoWork(n)

// 'recoveredValue' is returned. It will be the panic value if
// a panic occurred and was recovered, or nil otherwise.
return recoveredValue
}
1 change: 0 additions & 1 deletion internal/exercises/templates/36_epoch/epoch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,3 @@ func TestTimeToEpoch(t *testing.T) {
if got != want {
t.Fatalf("TimeToEpoch(%q) = %d, want %d", input, got, want)
}
}
25 changes: 25 additions & 0 deletions internal/exercises/templates/37_recover/recover.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package recover_exercise

// DoWork simulates a function that might panic if the input is negative.
func DoWork(n int) {
if n < 0 {
// A panic occurs if n is negative
panic("input cannot be negative")
}
// Normal, non-panicking code path...
}

// Run should call DoWork and safely recover from any panic
// that occurs during its execution, returning the recovered value.
// It should return nil if no panic occurred.
func Run(n int) (recoveredValue interface{}) {
// 1. Add your 'defer' statement here.
// 2. The deferred function should call recover() and assign the result
// to 'recoveredValue' if it is not nil.

// Your code here:

DoWork(n)

return recoveredValue
}
28 changes: 28 additions & 0 deletions internal/exercises/templates/37_recover/recover_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package recover_exercise

import (
"testing"
)

func TestRun_NoPanic(t *testing.T) {
t.Parallel()
result := Run(10)
if result != nil {
t.Errorf("Run(10) should not panic and should return nil. Got: %v", result)
}
}

func TestRun_WithPanic(t *testing.T) {
t.Parallel()
expectedPanicValue := "input cannot be negative"
result := Run(-5)

if result == nil {
t.Errorf("Run(-5) should panic and recover, returning the panic value. Got nil")
}

// Check if the recovered value is what we expected
if resultAsString, ok := result.(string); !ok || resultAsString != expectedPanicValue {
t.Errorf("Run(-5) recovered with unexpected value. Expected: %q, Got: %v (%T)", expectedPanicValue, result, result)
}
}