Skip to content

Commit 2f31c1f

Browse files
committed
Initial example of range over custom types
1 parent 6ed788f commit 2f31c1f

File tree

10 files changed

+330
-29
lines changed

10 files changed

+330
-29
lines changed

examples.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Interfaces
2222
Enums
2323
Struct Embedding
2424
Generics
25+
Range over Custom Types
2526
Errors
2627
Custom Errors
2728
Goroutines

examples/generics/generics.hash

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
d6b4792fc509f0dcd84f15ed92097f52a73eb877
2-
MNfKskDAZ6d
1+
1ad71763360077271687c5e9d147c89c0b580b0a
2+
7v7vElzhAeO
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Starting with version 1.23, Go has added support for
2+
// [iterators](https://go.dev/blog/range-functions),
3+
// which lets us range over custom types.
4+
5+
package main
6+
7+
import (
8+
"fmt"
9+
"iter"
10+
"slices"
11+
)
12+
13+
// Let's look at the `List` type from the
14+
// [previous example](generics) again. In that example
15+
// we had an `AllElements` method that returned a slice
16+
// of all elements in the list. With Go iterators, we
17+
// can do it better - as shown below.
18+
type List[T any] struct {
19+
head, tail *element[T]
20+
}
21+
22+
type element[T any] struct {
23+
next *element[T]
24+
val T
25+
}
26+
27+
func (lst *List[T]) Push(v T) {
28+
if lst.tail == nil {
29+
lst.head = &element[T]{val: v}
30+
lst.tail = lst.head
31+
} else {
32+
lst.tail.next = &element[T]{val: v}
33+
lst.tail = lst.tail.next
34+
}
35+
}
36+
37+
// All returns an _iterator_, which in Go is a function
38+
// with a special signature.
39+
func (lst *List[T]) All() iter.Seq[T] {
40+
return func(yield func(T) bool) {
41+
// The iterator function takes another function as
42+
// a parameter, called `yield` by convention (but
43+
// the name can be arbitrary). It will call `yield` for
44+
// every element we want to iterate over, and note `yield`'s
45+
// return value for a potential early termination.
46+
for e := lst.head; e != nil; e = e.next {
47+
if !yield(e.val) {
48+
return
49+
}
50+
}
51+
}
52+
}
53+
54+
func main() {
55+
lst := List[int]{}
56+
lst.Push(10)
57+
lst.Push(13)
58+
lst.Push(23)
59+
60+
// Since `List.All` returns an interator, it can be used
61+
// in a regular `range` loop!
62+
for e := range lst.All() {
63+
fmt.Println(e)
64+
}
65+
66+
// Packages like [slices](https://pkg.go.dev/slices) have
67+
// a number of useful functions to work with iterators.
68+
// For example, `Collect` takes any iterator and collects
69+
// all its values into a slice.
70+
all := slices.Collect(lst.All())
71+
fmt.Println("all:", all)
72+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
28edd55763e81476f37e68085f5f79555c15ffe8
2+
Dc3AddmC8Jc
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
10
2+
13
3+
23
4+
all: [10 13 23]
5+

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/mmcgrana/gobyexample
22

3-
go 1.22.0
3+
go 1.23.0
44

55
require (
66
github.com/alecthomas/chroma/v2 v2.10.0

public/errors

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/generics

Lines changed: 14 additions & 25 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/index.html

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)