Skip to content

Commit

Permalink
Initial example of range over custom types
Browse files Browse the repository at this point in the history
  • Loading branch information
eliben committed Aug 21, 2024
1 parent 6ed788f commit 2f31c1f
Show file tree
Hide file tree
Showing 10 changed files with 330 additions and 29 deletions.
1 change: 1 addition & 0 deletions examples.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Interfaces
Enums
Struct Embedding
Generics
Range over Custom Types
Errors
Custom Errors
Goroutines
Expand Down
4 changes: 2 additions & 2 deletions examples/generics/generics.hash
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
d6b4792fc509f0dcd84f15ed92097f52a73eb877
MNfKskDAZ6d
1ad71763360077271687c5e9d147c89c0b580b0a
7v7vElzhAeO
72 changes: 72 additions & 0 deletions examples/range-over-custom-types/range-over-custom-types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Starting with version 1.23, Go has added support for
// [iterators](https://go.dev/blog/range-functions),
// which lets us range over custom types.

package main

import (
"fmt"
"iter"
"slices"
)

// Let's look at the `List` type from the
// [previous example](generics) again. In that example
// we had an `AllElements` method that returned a slice
// of all elements in the list. With Go iterators, we
// can do it better - as shown below.
type List[T any] struct {
head, tail *element[T]
}

type element[T any] struct {
next *element[T]
val T
}

func (lst *List[T]) Push(v T) {
if lst.tail == nil {
lst.head = &element[T]{val: v}
lst.tail = lst.head
} else {
lst.tail.next = &element[T]{val: v}
lst.tail = lst.tail.next
}
}

// All returns an _iterator_, which in Go is a function
// with a special signature.
func (lst *List[T]) All() iter.Seq[T] {
return func(yield func(T) bool) {
// The iterator function takes another function as
// a parameter, called `yield` by convention (but
// the name can be arbitrary). It will call `yield` for
// every element we want to iterate over, and note `yield`'s
// return value for a potential early termination.
for e := lst.head; e != nil; e = e.next {
if !yield(e.val) {
return
}
}
}
}

func main() {
lst := List[int]{}
lst.Push(10)
lst.Push(13)
lst.Push(23)

// Since `List.All` returns an interator, it can be used
// in a regular `range` loop!
for e := range lst.All() {
fmt.Println(e)
}

// Packages like [slices](https://pkg.go.dev/slices) have
// a number of useful functions to work with iterators.
// For example, `Collect` takes any iterator and collects
// all its values into a slice.
all := slices.Collect(lst.All())
fmt.Println("all:", all)
}
2 changes: 2 additions & 0 deletions examples/range-over-custom-types/range-over-custom-types.hash
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
28edd55763e81476f37e68085f5f79555c15ffe8
Dc3AddmC8Jc
5 changes: 5 additions & 0 deletions examples/range-over-custom-types/range-over-custom-types.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
10
13
23
all: [10 13 23]

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/mmcgrana/gobyexample

go 1.22.0
go 1.23.0

require (
github.com/alecthomas/chroma/v2 v2.10.0
Expand Down
2 changes: 1 addition & 1 deletion public/errors

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 14 additions & 25 deletions public/generics

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions public/index.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 2f31c1f

Please sign in to comment.