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.
1 min read
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:
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:
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:
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.
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.
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.
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.
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:
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.
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.