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. This is most common with pointer types. For example:
func (*Result) Next() *User
But there are other patterns to consider, as well. One of the most popular is to have a Next() method that only advances to the next item, leaving processing of the item to other method(s). A common example can be found in the database/sql package:
(*Rows) Next() booladvances to the next item, or returns false in case of error or if there are no more items.- `(*Rows) Scan(…any) error processes the current result.
This pattern is useful in a few main situations:
- You need to report a per-item error, as distinct from an iterator-general error. This is the case in the database package.
Scan()can return an error, without aborting the iterator. - You have different ways of processing an item. This only half way applies to
database/sql. In addition to theScanmethod, the*Rowstype also exposes ColumyTypes, and Columns, and NextResultSet. These are for advanced use well beyond the scope of today’s discussion, but the custom iterator pattern grants this flexiblity, and that’s what we care about here. - Probably less common, but you may want to not process some items. If you know you’ll discard an item, you may be able to save some CPU and memory by not parsing it at all, and advancing directly to the next item.