Iterators

4 min read


Errors with range over func

Last week I introduced the topic of range-over-func as an iterator pattern. Let’s look at how error handling differs with this approach. For context, let’s compare the custom iterator approach, which has three distinct errors to check: 1. The initial function call, 2. while iterating over each row, 3. finally at the end, to ensure iteration completed successfully. orders, err := db.Orders(ctx, userID) if err != nil { return err } defer orders.

Iterators

2 min read


Range over func

Since Go 1.23, we’ve had a new way we can implement iterators. I’ve written previously about it if you’re interested. But today we’ll take a glance at how it affects our Orders example: orders, err := db.Orders(ctx, userID) if err != nil { return err } for order, err := range orders { if err != nil { return err } /* Do something with each order */ } Unless you’re already quite familiar with the range-over-func feature, it’s probably not immediately clear what’s going on here, just by looking at the code.

Iterators

2 min read


Custom iterators

Until recently, custom iterators were probably the most common way to iterate over a list of elements that might trigger an error. Several examples exist in the standard library. Perhaps the most well known would be the sql.Rows type, which provides (among others), the following methods: Next() bool — Advances to the next item Scan(...any) error — Processes the current item Err() error — Reports an iteration error Close() error — Closes iterator, possibly before the last item has been processed These four methods are pretty standard, in any custom iterator implementation, though Scan() will typically be replaced with an implementation-specific method to process the current result, and in some cases Err() and Close() may be combined.

Iterators

3 min read


Iterating over channels

For this discussion of iterators, let’s establish a baseline example. It’s made up, but realistic and common: A database method that returns all user orders. We’ll be experimenting with different function signatures, but in general, this is what we can imagine it will look like: func (DB) Orders(ctx context.Context, userID string) ([]*Order, error) And we would consume it with code something like this: orders, err := db.Orders(ctx, userID) if err !

Iterators

2 min read


Iterator patterns

I’m going to change gears from the previous discussion about goroutines, to different ways of iterating. This can closely relate to goroutines, so we’ll bounce back and forth a bit, I suspect. Meanwhile, if you have any questions specifically about goroutines that I didn’t cover, send me an email. I’ll be happy to fill in those gaps! First off, before really diving into iterator patterns, let’s talk about why we might want iterators.

Go Routines

2 min read


Limiting goroutines, chapter 2

Last time I introduced the SetLimit feature of the errgroup package. But this feature is not sufficient if you ever want to set some sort of shared or global limit on the number of jobs doing a task. For a more general-purpose rate limiting mechanism, I often reach for the x/sync/semaphore package. Let’s modify the example from last time, to introduce a global limit using this library: import ( // other impots "golang.

Go Routines

2 min read


Limiting goroutines

The errgroup package we’ve been looking at lately has one more trick up its sleeve: Limiting the number of running goroutines. Let’s imagine we’re building an HTTP handler that responds with a list of all customer orders. Doing so, requires making a number of calls to the ‘orders’ microservice. Some customers may have made hundreds or thousands of orders. We don’t want to look them all up simultaneously, so we’ll limit our client to 10 concurrent requests.

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.

Go Routines

1 min read


Aborting goroutines on error

I’m sorry for the delay in writing. Last week I was under the weather. Let’s pick up our discussion of goroutines and errors. Last time, I talked about errgroup, which makes it easy to run an arbitrary number of goroutines, and gather the first error (if any) that one returns. But what if that error renders the rest of the goroutines obsolete? A very common case is that any error means that the rest of the processing becomes moot.

Go Routines

2 min read


Simpler error handling in goroutines

Join me later today at 3:00pm EDT (19:00 UTC), for more live coding. Subscribe on YouTube and hit the Notify me button so you don’t miss it! Yesterday we looked at how to handle errors from multiple goroutines using an error channel. While functional, the approach is somewhat complicated, and easy to get wrong. Enter errgroup. While not part of the standard library, it is maintained by the Go team, and is widely used in the Go ecosystem.

Go Routines

3 min read


Goroutines and error channels

Join me tomorrow, August 29, at 3:00pm EDT (19:00 UTC), for more live coding. Subscribe on YouTube and hit the Notify me button so you don’t miss it! Last week we looked at how to handle errors from multiple goroutines using a mutex and a shared error variable. While this works for many common scenarios, it has some limitations. For example, it only captures the first error encountered and ignores any subsequent errors.

Go Routines

3 min read


But what about errors?

Join me today, August 22, at 3:00pm EDT (19:00 UTC), more live coding. Subscribe on YouTube and hit the Notify me button so you don’t miss it! We’ve been looking at different ways of managing multiple goroutines. But what if one (or more) the functions called in a goroutine might return an error? So far, we don’t have a solution to that in our toolbox. There are a few different ways we can accomplish this.