Other

1 min read


GopherCon Early Gopher Tickets available

I normally try to share Go tips and tricks, mini tutorials on this list, rather than promoting things. But I’m going to make an exception today, but to promote something that itself provides tips, tricks, and mini tutorials (and so much more)! Earlier this week, GopherCon 2026 tickets went on sale, and through the end of the month, Early Gopher tickets are on sale for $200 less than the regular price.


Happy late new year

I kinda fell off the planet for a while with family, holidays, and… writers’s block. But I’m back now, and prepared to take the time to come up with (hopefully) interesting things to write about again. Today I’m going to talk about a new feature coming in Go 1.26, which relates to my series earlier in 2025 about contexts. The new feature is mentioned very briefly in the release notes:

Iterators

2 min read


Iterator callbacks

Today I want to expand with a thought I touched on yesterday: Callbacks with iterators. Yesterday’s context was error handling. But I’ve found callbacks with iterators to be very valuable in a slightly different case. To understand the problem callbacks have helped me solve, we need to move away from our grep example to something a bit more involved. Let’s imagine we’re querying a REST API, which returns paginated results. A typical response body might look something like this, in JSON:

Iterators

3 min read


Alternatives to iter.Seq3

Last time we modified our range-over-func iterator to return a iter.Seq2, so that it could include a possible error value for each iteration. But this isn’t the only way to handle errors with range-over-func. And in fact, in some cases, it may not even be possible! Suppose you’re ranging over a key/value pair, for example. There is no iter.Seq3 option to return three values per iteration. To illustrate, let’s update our grep to return the line number, and the matching line:

Iterators

3 min read


Handling errors during iteration with range-over-func

Yesterday we looked at a range-over-func iterator that was missing a vital piece: Error handling during iteration. I know of three possible solutions to this problem, and today we’ll look at the simplest of them, which I typically recommend: Using iter.Seq2. We’ve already looked at this pattern from the consumer’s perspective. Today we’ll see how the implementation works. func grep(r io.Reader, pattern string) (iter.Seq2[string, error], error) { re, err := regexp.

Iterators

2 min read


Implementing a range-over-func iterator

It’s finally time to look at how we implement a range-over-func iterator. I’d venture a guess this should be the go-to pattern for most iterators, unless or until you have special needs that it won’t address. And, of course, we’ll discuss some of those in the near future, as well. First, here’s how our grep implementation looks using range-over-func: func grep(r io.Reader, pattern string) (iter.Seq[string], error) { re, err := regexp.

Iterators

2 min read


Separating iteration from advancement

Last week we looked at a simple custom iterator pattern: type Result struct {/* ... */} func (*Result) Next() (string, bool) func (*Result) Err() error But let’s talk about a few variations, and when they might make sense. First, I already mentioned last week that in some cases we could simply eliminate the bool value if the zero value of the iterated value can serve as an indication that iteration has completed.

Subscribe to Boldly Go: Daily

Every day I'll send you advice to improve your understanding of Go. Don't miss out! I will respect your inbox, and honor my privacy policy.

Unsure? Browse the archive.

Iterators

3 min read


Building a custom iterator

We’ve looked at using channels as iterators, and found they’re hardly ideal. Let’s look at the next obvious answer: custom iterators. Before range-over-func, which we’ll get to next, custom iterators were really the only meaningful solution. And they still remain a very viable one, because of their great flexibility. Let’s start with a simple implementation of a custom iterator version of our grep function (see it in the playground): type Result struct { re *regexp.

Iterators

3 min read


Closing a channel iterator early

I’ve been away for a while. Last week I spoke at GoWest 2025, and have been just generally busy. But now I’m ready to pick up on the topic I started nearly two weeks ago: Drawbacks of channel-based iterators! In addition to the issue of error handling with a channel-based iterator, there’s the potentially stickier issue of how to abort iterating early. To illustrate, let’s look at some code that consumes our grep iterator, but stops processing after the first result:

Iterators

3 min read


Implementing a channel-based iterator

Today let’s look at re-implementing our non-iterating grep function using a channel for iteration. First the code: func grep(r io.Reader, pattern string) (chan <- string, error) { re, err := regexp.Compile(pattern) if err != nil { return nil, err } matches := make(chan string) go func() { defer close(matches) scanner := bufio.NewScanner(r) var matches []string for scanner.Scan() { line := scanner.Text() if re.MatchString(line) { matches <- line } } if err := scanner.

Iterators

1 min read


Implementing iterators

The last few weeks I’ve been talking about different iterator patterns, but from the perspective of consuming iterators. Let’s switch angles now, and begin talking about how to implement iterators. To illustrate, let’s use a simple example of a grep utility. It will read an io.Reader, and return any lines that match a regular expression input. Here’s a simple implementation, that returns all results at once (no iteration): func grep(r io.