yield callback again if a call to the callback has ever returned false
yield callback of an iterator function if the iterator function has returned
yield callback function concurrently needs careful synchronizations
func(func() bool)
func(func(V) bool)
func(func(K, V) bool)
// K and V both denote types.
yield by convention. Such ballback functions might take zero, one or two parameters and always return a bool result.
abc (since Go 1.23).
package main
import "fmt"
func main() {
for v := range abc {
fmt.Print(v)
}
}
func abc(yield func(string) bool) {
if yield("a") {
if yield("b") {
yield("c")
}
}
}
abc is used as an iterator here. Its parameter yield is a callback function which takes a string parameter and returns a bool result.
"a", "b" and "c" are in turn passed to the string paramter and are used as the values of (the instances of) the loop variable v.
yield function. Just note that, if the execution of a loop step during ranging over an iterator function terminates the whole loop (such as break the loop), then the corresponding yield call returns false (to the iterator function); otherwise the corresponding yield call returns true.
func abc(yield func(string) bool) {
_ = yield("a") && yield("b") && yield("c")
}
yield callback again if a call to the callback has ever returned false
package main
import "fmt"
func main() {
for v := range ab {
fmt.Println(v)
break // remove this line to avoid crashing.
}
}
func ab(yield func(string) bool) {
var done = yield("a")
fmt.Println(done) // false
yield("b") // runtime error: range function
// continued iteration after function
// for loop body returned false.
}
go vet also doesn't check such invalid uses (up to v1.24.x).
yield callback of an iterator function if the iterator function has returned
package main
import "fmt"
var f func(string) bool
func main() {
for v := range abc {
fmt.Print(v)
}
f("d") // runtime error: range function
// continued iteration after
// whole loop exit
}
func abc(yield func(string) bool) {
_ = yield("a") && yield("b") && yield("c")
f = yield
}
yield callback function concurrently needs careful synchronizations
yield callback function are not synchronized by default. You need to do the synchronizations by yourself.
package main
import "fmt"
import "sync"
var c = make(chan int)
func main() {
// runtime error: range function continued iteration after loop body panic
for v := range oneHundred {
<-c
fmt.Println(v)
}
}
func oneHundred(yield func(int) bool) {
var wg sync.WaitGroup
for v := range 100 {
wg.Add(1)
go func() {
defer wg.Done()
yield(v)
}()
}
close(c)
wg.Wait()
}
func abc(yield func(int) bool) {
var wg sync.WaitGroup
var mu sync.Mutex // use a mutex to sync
for v := range 100 {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock() // prevent yield being
defer mu.Unlock() // called parallelly.
yield(v)
}()
}
close(c)
wg.Wait()
}
yield callback functions in other goroutines.
0a1b2c.
package main
import "fmt"
func main() {
for v := range abc {
fmt.Print(v)
}
}
func abc(yield func(string) bool) {
for i, v := range "abc" {
fmt.Print(i)
if (!yield(string(v))) {
break
}
}
}
main function. Finally, the program prints 4194304, 4194305 and 4194306 in turn.
package main
func wrap(it func(yield func(int) bool)) func(yield func(int) bool) {
return func(yield func(int) bool) {
for v := range it {
yield(v+1)
}
}
}
func main() {
iterator := func(yield func(int) bool) {
_ = yield(0) && yield(1) && yield (2)
}
for range 1 << 22 { // 23 crashes the program
iterator = wrap(iterator)
}
for v := range iterator {
println(v)
}
}
func abc(yield func(string) bool) {
_ = yield("a") && yield("b") && yield("c")
}
for v := range abc {
fmt.Print(v)
}
abc(func(v string) bool {
fmt.Print(v)
return true
})
func abcThenPanic(yield func(string) bool) {
_ = yield("a") && yield("b") && yield("c")
panic("bye")
}
for v := range abcThenPanic {
defer func() {
recover()
}()
fmt.Print(v)
}
abcThenPanic(func(v string) bool {
defer func() {
recover()
}()
fmt.Print(v)
return true
})
goto or return statements, the equivalence between the two ways becomes even less likely.
package main
func main() {
defer func() {
var v = recover()
println(v == 123)
}()
for range iter {
panic(123)
}
}
func iter(yield func() bool) {
defer func() {
recover()
}()
yield()
}
false.
package main
func main() {
defer func() {
var v = recover()
println(v == 123)
}()
for range iter {}
panic(123)
}
func iter(yield func() bool) {
defer func() {
recover()
}()
yield()
}
true.
package main
import "fmt"
func main() {
defer foo()
panic(123)
}
func foo() {
for range iter {
fmt.Println(recover())
}
}
func iter(yield func() bool) {
yield()
}
<nil> then crashes.
package main
import "fmt"
func main() {
defer foo()
panic(123)
}
func foo() {
for range 1 {
fmt.Println(recover())
}
}
123 then exits normally.
The Go 101 project is hosted on Github. Welcome to improve Go 101 articles by submitting corrections for all kinds of mistakes, such as typos, grammar errors, wording inaccuracies, description flaws, code bugs and broken links.
If you would like to learn some Go details and facts every serveral days, please follow Go 101's official Twitter account @zigo_101.