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 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.