├── README.md ├── go.mod ├── image ├── main.go └── main_test.go ├── list └── main.go ├── mapiter └── main.go ├── merge └── main.go ├── nostr ├── go.mod ├── go.sum └── main.go ├── rangeint └── main.go ├── rows ├── go.mod ├── go.sum └── main.go ├── timefrom └── main.go └── zip └── main.go /README.md: -------------------------------------------------------------------------------- 1 | # go-for-range-experiment-example 2 | 3 | Example code for Go generics. 4 | 5 | ## Usage 6 | 7 | for go1.22 8 | ``` 9 | $ GOEXPERIMENT=rangefunc go build 10 | ``` 11 | 12 | for go1.23 13 | ``` 14 | $ go build 15 | ``` 16 | 17 | ## Requirements 18 | 19 | Go 1.22 or later 20 | 21 | ## License 22 | 23 | MIT 24 | 25 | ## Author 26 | 27 | Yasuhiro Matsumoto (a.k.a. mattn) 28 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-for-range-experiment-example 2 | 3 | go 1.23 4 | -------------------------------------------------------------------------------- /image/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "image" 5 | "iter" 6 | ) 7 | 8 | func Points(img image.Image) iter.Seq[image.Point] { 9 | return func(yield func(image.Point) bool) { 10 | bounds := img.Bounds() 11 | for y := bounds.Min.Y; y < bounds.Max.Y; y++ { 12 | for x := bounds.Min.X; x < bounds.Max.X; x++ { 13 | if !yield(image.Point{X: x, Y: y}) { 14 | return 15 | } 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /image/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "image" 6 | ) 7 | 8 | func ExamplePoints() { 9 | img := image.NewRGBA(image.Rectangle{ 10 | Min: image.Point{X: 2, Y: 5}, 11 | Max: image.Point{X: 5, Y: 8}, 12 | }) 13 | 14 | for p := range Points(img) { 15 | fmt.Println(p) 16 | // eg. r, g, b, a := img.At(p.X, p.Y).RGBA() 17 | } 18 | 19 | // Output: (2,5) 20 | // (3,5) 21 | // (4,5) 22 | // (2,6) 23 | // (3,6) 24 | // (4,6) 25 | // (2,7) 26 | // (3,7) 27 | // (4,7) 28 | } 29 | -------------------------------------------------------------------------------- /list/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "iter" 5 | ) 6 | 7 | type List[T any] struct { 8 | Value T 9 | next *List[T] 10 | } 11 | 12 | func (l *List[T]) Add(v T) { 13 | last := l 14 | for last.next != nil { 15 | last = last.next 16 | } 17 | last.next = &List[T]{ 18 | Value: v, 19 | } 20 | } 21 | 22 | func (l *List[T]) All() iter.Seq[List[T]] { 23 | return func(yield func(List[T]) bool) { 24 | for l.next != nil { 25 | l = l.next 26 | if !yield(*l) { 27 | return 28 | } 29 | } 30 | } 31 | } 32 | 33 | func main() { 34 | var list List[int] 35 | list.Add(1) 36 | list.Add(2) 37 | list.Add(3) 38 | list.Add(4) 39 | for v := range list.All() { 40 | println(v.Value) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /mapiter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | func main() { 9 | var m sync.Map 10 | 11 | m.Store("alice", 11) 12 | m.Store("bob", 12) 13 | m.Store("cindy", 13) 14 | for key, val := range m.Range { 15 | fmt.Println(key, val) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /merge/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "iter" 6 | "slices" 7 | ) 8 | 9 | func merge[T any](seqs ...iter.Seq[T]) iter.Seq[T] { 10 | return func(yield func(T) bool) { 11 | nexts := make([]func() (T, bool), len(seqs)) 12 | stops := make([]func(), len(seqs)) 13 | for i, seq := range seqs { 14 | nexts[i], stops[i] = iter.Pull(seq) 15 | defer stops[i]() 16 | } 17 | for len(nexts) > 0 { 18 | v, ok := nexts[0]() 19 | if !ok { 20 | nexts = nexts[1:] 21 | continue 22 | } 23 | if !yield(v) { 24 | break 25 | } 26 | nexts = append(nexts[1:], nexts[0]) 27 | } 28 | } 29 | } 30 | 31 | func ofChan[T any](c <-chan T) iter.Seq[T] { 32 | return func(yield func(T) bool) { 33 | for v := range c { 34 | if !yield(v) { 35 | return 36 | } 37 | } 38 | } 39 | } 40 | 41 | func main() { 42 | a1 := []int{1, 5, 9} 43 | a2 := []int{2, 6, 10, 13} 44 | a3 := []int{3, 7, 11} 45 | a4 := make(chan int) 46 | 47 | go func() { 48 | defer close(a4) 49 | a4 <- 4 50 | a4 <- 8 51 | a4 <- 12 52 | a4 <- 14 53 | }() 54 | 55 | for v := range merge(slices.Values(a1), slices.Values(a2), slices.Values(a3), ofChan(a4)) { 56 | fmt.Println(v) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /nostr/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-for-range-experiment-example/nostr 2 | 3 | go 1.23 4 | 5 | require github.com/nbd-wtf/go-nostr v0.28.3 6 | 7 | require ( 8 | github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect 9 | github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect 10 | github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect 11 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect 12 | github.com/gobwas/httphead v0.1.0 // indirect 13 | github.com/gobwas/pool v0.2.1 // indirect 14 | github.com/gobwas/ws v1.3.2 // indirect 15 | github.com/josharian/intern v1.0.0 // indirect 16 | github.com/mailru/easyjson v0.7.7 // indirect 17 | github.com/puzpuzpuz/xsync/v3 v3.0.2 // indirect 18 | github.com/tidwall/gjson v1.17.0 // indirect 19 | github.com/tidwall/match v1.1.1 // indirect 20 | github.com/tidwall/pretty v1.2.1 // indirect 21 | golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 // indirect 22 | golang.org/x/sys v0.16.0 // indirect 23 | ) 24 | -------------------------------------------------------------------------------- /nostr/go.sum: -------------------------------------------------------------------------------- 1 | github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= 2 | github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= 3 | github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= 4 | github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 5 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 6 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 7 | github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= 8 | github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= 9 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= 10 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= 11 | github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= 12 | github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= 13 | github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= 14 | github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= 15 | github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q= 16 | github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= 17 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 18 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 19 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 20 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 21 | github.com/nbd-wtf/go-nostr v0.28.3 h1:sFG+OTwQfm9fr+JoHFfuQ1HGUAuSIIrx0hQAyq9EU58= 22 | github.com/nbd-wtf/go-nostr v0.28.3/go.mod h1:l9NRRaHPN+QwkqrjNKhnfYjQ0+nKP1xZrVxePPGUs+A= 23 | github.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew= 24 | github.com/puzpuzpuz/xsync/v3 v3.0.2/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= 25 | github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= 26 | github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 27 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 28 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 29 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 30 | github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= 31 | github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 32 | golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFcdWLGIKnZA4IXNFSrvo= 33 | golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= 34 | golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= 35 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 36 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 37 | golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= 38 | golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 39 | -------------------------------------------------------------------------------- /nostr/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "iter" 6 | "log" 7 | 8 | "github.com/nbd-wtf/go-nostr" 9 | ) 10 | 11 | func relayFunc(url string) iter.Seq[*nostr.Event] { 12 | return func(yield func(ev *nostr.Event) bool) { 13 | ctx := context.Background() 14 | relay, err := nostr.RelayConnect(ctx, url) 15 | if err != nil { 16 | log.Fatal(err) 17 | } 18 | defer relay.Close() 19 | 20 | now := nostr.Now() 21 | filter := nostr.Filters{ 22 | { 23 | Kinds: []int{nostr.KindTextNote}, 24 | Since: &now, 25 | }, 26 | } 27 | sub, err := relay.Subscribe(ctx, filter) 28 | if err != nil { 29 | log.Println(err) 30 | return 31 | } 32 | for { 33 | ev, ok := <-sub.Events 34 | if !ok || !yield(ev) { 35 | return 36 | } 37 | } 38 | } 39 | } 40 | 41 | func main() { 42 | for note := range relayFunc("wss://yabu.me") { 43 | println(note.Content) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /rangeint/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | for i := range 10 { 5 | println(i) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /rows/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mattn/go-for-range-experiment-example/rows 2 | 3 | go 1.23.0 4 | 5 | require github.com/mattn/go-sqlite3 v1.14.22 // indirect 6 | -------------------------------------------------------------------------------- /rows/go.sum: -------------------------------------------------------------------------------- 1 | github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= 2 | github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= 3 | -------------------------------------------------------------------------------- /rows/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "database/sql/driver" 6 | "fmt" 7 | "log" 8 | "reflect" 9 | 10 | _ "github.com/mattn/go-sqlite3" 11 | ) 12 | 13 | func ScanRows(rows *sql.Rows) func(func(tv []any) bool) { 14 | columns, err := rows.ColumnTypes() 15 | if err != nil { 16 | return nil 17 | } 18 | 19 | values := make([]any, len(columns)) 20 | object := map[string]any{} 21 | 22 | for i, column := range columns { 23 | object[column.Name()] = reflect.New(column.ScanType()).Interface() 24 | values[i] = object[column.Name()] 25 | } 26 | 27 | return func(yield func([]any) bool) { 28 | for rows.Next() { 29 | err = rows.Scan(values...) 30 | if err != nil { 31 | return 32 | } 33 | ret := make([]any, len(values)) 34 | for i, v := range values { 35 | if vv, err := v.(driver.Valuer).Value(); err == nil { 36 | ret[i] = vv 37 | } 38 | } 39 | if !yield(ret) { 40 | return 41 | } 42 | } 43 | } 44 | } 45 | 46 | func main() { 47 | db, err := sql.Open("sqlite3", ":memory:") 48 | if err != nil { 49 | log.Fatal(err) 50 | } 51 | defer db.Close() 52 | 53 | _, err = db.Exec(`CREATE TABLE user(id integer primary key autoincrement, name text, age integer, type integer not null)`) 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | 58 | _, err = db.Exec(`DELETE FROM user`) 59 | if err != nil { 60 | log.Fatal(err) 61 | } 62 | _, err = db.Exec(`INSERT INTO user(name, age, type) values('John', 20, 0), ('Mike', 25, 1), ('Bob', null, 1)`) 63 | if err != nil { 64 | log.Fatal(err) 65 | } 66 | 67 | rows, err := db.Query(`SELECT id, name, age FROM user ORDER BY id`) 68 | if err != nil { 69 | log.Fatal(err) 70 | } 71 | defer rows.Close() 72 | 73 | for values := range ScanRows(rows) { 74 | fmt.Printf("%v, %v, %v\n", values[0], values[1], values[2]) 75 | } 76 | if rows.Err() != nil { 77 | log.Fatal(err) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /timefrom/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func TimeFrom(s time.Duration, d time.Duration) func(func(time.Time) bool) { 9 | return func(yield func(time.Time) bool) { 10 | start := time.Now().Add(s) 11 | for start.Before(time.Now()) { 12 | if !yield(start) { 13 | return 14 | } 15 | start = start.Add(d) 16 | } 17 | } 18 | } 19 | 20 | func main() { 21 | for v := range TimeFrom(-3*time.Minute, time.Minute) { 22 | fmt.Println(v) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /zip/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "iter" 7 | "os" 8 | "os/exec" 9 | "time" 10 | ) 11 | 12 | func OutputLines(p string, args ...string) func(func(string) bool) { 13 | return func(yield func(string) bool) { 14 | cmd := exec.Command(p, args...) 15 | r, err := cmd.StdoutPipe() 16 | if err != nil { 17 | fmt.Fprintln(os.Stderr, err) 18 | return 19 | } 20 | defer r.Close() 21 | err = cmd.Start() 22 | if err != nil { 23 | fmt.Fprintln(os.Stderr, err) 24 | return 25 | } 26 | scanner := bufio.NewScanner(r) 27 | for scanner.Scan() { 28 | if !yield(scanner.Text()) { 29 | return 30 | } 31 | } 32 | } 33 | } 34 | 35 | func ZipPull[V1, V2 any]( 36 | left func(yield func(v V1) bool), 37 | right func(yield func(v V2) bool), 38 | ) func(yield func(l V1, r V2) bool) { 39 | return func(yield func(l V1, r V2) bool) { 40 | nextL, stopL := iter.Pull(left) 41 | nextR, stopR := iter.Pull(right) 42 | defer stopL() 43 | defer stopR() 44 | 45 | for { 46 | l, lOk := nextL() 47 | r, rOk := nextR() 48 | 49 | if !lOk || !rOk { 50 | return 51 | } 52 | if !yield(l, r) { 53 | return 54 | } 55 | } 56 | } 57 | } 58 | 59 | func main() { 60 | tic := OutputLines("yes", "tic") 61 | toc := OutputLines("yes", "toc") 62 | for l, r := range ZipPull(tic, toc) { 63 | fmt.Println(l, r) 64 | time.Sleep(time.Second) 65 | } 66 | } 67 | --------------------------------------------------------------------------------