| // run | |
| // Copyright 2026 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // A recover called in the body of a range-over-func loop takes effect, | |
| // just as it would anywhere else in the function containing the loop. | |
| // The body runs in a frame of its own, called from the iterator, but in | |
| // the source it is part of that function, and the frames the compiler | |
| // uses to run the loop must not change what recover means. | |
| package main | |
| import "fmt" | |
| func iter(yield func(int) bool) { yield(0) } | |
| var fails int | |
| func check(name string, got, want any) { | |
| if got != want { | |
| fmt.Printf("%s = %v, want %v\n", name, got, want) | |
| fails++ | |
| } | |
| } | |
| // recovered reports what a call of f, deferred by a panicking function, | |
| // recovered, or nil if the panic went unrecovered. | |
| func recovered(f func(*any)) (got any) { | |
| defer func() { | |
| if recover() != nil { | |
| got = nil // the panic escaped f | |
| } | |
| }() | |
| var r any | |
| func() { | |
| defer f(&r) | |
| panic("boom") | |
| }() | |
| return r | |
| } | |
| // A recover in a loop body recovers, and returns the panic value. | |
| func body(r *any) { | |
| for range iter { | |
| *r = recover() | |
| } | |
| } | |
| // So does one in the body of a loop nested in another loop's body. | |
| func nested(r *any) { | |
| for range iter { | |
| for range iter { | |
| *r = recover() | |
| } | |
| } | |
| } | |
| // A recover in a func literal in the body does not: it is two calls away | |
| // from the deferred function, not one, exactly as outside a loop. | |
| func inClosure(r *any) { | |
| for range iter { | |
| func() { *r = recover() }() | |
| } | |
| } | |
| // A recover in a loop in a function that was not the one deferred does | |
| // not recover either. | |
| func inCallee(r *any) { | |
| helper(r) | |
| } | |
| func helper(r *any) { | |
| for range iter { | |
| *r = recover() | |
| } | |
| } | |
| // Ranging over something other than a function must behave the same way. | |
| func overInt(r *any) { | |
| for range 1 { | |
| *r = recover() | |
| } | |
| } | |
| func overSlice(r *any) { | |
| for range []int{0} { | |
| *r = recover() | |
| } | |
| } | |
| func overChan(r *any) { | |
| ch := make(chan int, 1) | |
| ch <- 0 | |
| close(ch) | |
| for range ch { | |
| *r = recover() | |
| } | |
| } | |
| // A defer in the body defers to the containing function, and a recover in | |
| // the body still works alongside it. | |
| func withDefer(r *any) { | |
| for range iter { | |
| defer func() { *r = "deferred ran" }() | |
| if recover() == nil { | |
| *r = "did not recover" | |
| return | |
| } | |
| } | |
| } | |
| // Recovering twice: the second call reports no panic left to recover. | |
| func twice(r *any) { | |
| for range iter { | |
| first := recover() | |
| second := recover() | |
| *r = fmt.Sprint(first, " ", second) | |
| } | |
| } | |
| // A whole panic and recover inside the body, before recovering the panic | |
| // the body was deferred for. | |
| func inner(r *any) { | |
| for range iter { | |
| func() { | |
| defer func() { recover() }() | |
| panic("inner") | |
| }() | |
| *r = recover() | |
| } | |
| } | |
| func main() { | |
| check("body", recovered(body), "boom") | |
| check("nested", recovered(nested), "boom") | |
| check("inClosure", recovered(inClosure), nil) | |
| check("inCallee", recovered(inCallee), nil) | |
| check("overInt", recovered(overInt), "boom") | |
| check("overSlice", recovered(overSlice), "boom") | |
| check("overChan", recovered(overChan), "boom") | |
| check("withDefer", recovered(withDefer), "deferred ran") | |
| check("twice", recovered(twice), "boom <nil>") | |
| check("inner", recovered(inner), "boom") | |
| // With nothing panicking, a recover in a loop body returns nil and the | |
| // loop runs as usual. | |
| n := 0 | |
| var got any = "not nil" | |
| for range iter { | |
| got = recover() | |
| n++ | |
| } | |
| check("no panic: recover()", got, nil) | |
| check("no panic: iterations", n, 1) | |
| if fails > 0 { | |
| panic("failed") | |
| } | |
| } |