AL+

Language: RO (română) EN (English)

Email us: paul@alplustech.com

Looking back at the week of 7–13 August 2024

Go 1.23 iterators should simplify your public API

Go 1.23 gave library authors range over functions. Choose iterators where they clarify traversal, early stopping and ownership, without rewriting slice loops.

Citiți articolul în română

A library caller should not need to learn a new traversal protocol for every collection. Go 1.23 made it possible to expose custom traversal through an ordinary range loop. Use that capability when it improves the caller's contract, especially when the caller may stop before consuming everything. Existing slice loops need no migration merely because another interface became available.

This is a November 2026 retrospective on Go 1.23, released on 13 August 2024. The maintainers' longer explanation followed on 20 August. That later article helps explain the design; it does not move the release into a different week or establish how widely the feature was adopted.

Start with what callers need

Suppose, as an illustrative API design, a library exposes traversal of an ordered collection. Some callers inspect every item; others stop at the first match. Returning a slice is easy to understand, but may require preparing the whole result before returning. A callback can avoid that requirement, but gives the library its own loop convention. A channel communicates values through a concurrency mechanism even when traversal itself needs no concurrent producer.

An iterator is another contract to compare, rather than the inevitable winner. If the collection already lives in a slice and callers need indexing, a slice may expose exactly the useful operation. If producer and consumer genuinely run independently and must coordinate through channel operations, keeping the channel can be appropriate. The public interface should reveal the work that exists.

The 1.23 release notes describe range over functions with supported yield signatures, including one-value and two-value forms. The new iter package names the common sequence shapes Seq and Seq2. This provides shared vocabulary for library interfaces, without making every function an iterator.

Stopping is part of the promise

The tagged Go 1.23 iterator source and package documentation explain that the yield callback returns false when traversal should stop. An implementation must honor that result. Otherwise a caller's early exit no longer corresponds to the producer finishing its work as expected.

For the illustrative collection, the important review is whether stopping after the first match avoids visiting later elements and releases anything the traversal owns. That is a proposed check for a concrete implementation, not a benchmark performed here. A shorter loop is useful only if its hidden work and lifetime remain understandable.

Do not equate stopping between yielded items with cancelling a blocked external operation. If traversal reads from a network service, the API still needs a policy for waiting, cancellation and failure. iter.Seq does not carry an error result as part of its type. The surrounding API must explain how a caller learns that traversal was incomplete.

That explanation may be a separate error method, an explicit result type or another interface entirely. Choose deliberately and document the condition. An iterator that makes success pleasant while making failure invisible is a poor simplification for business software.

Ownership survives the syntax change

An iterator may refer to data that changes. Callers need to know whether they see a snapshot, whether mutation during traversal is allowed and whether a returned value remains valid after the next step. Range syntax does not answer those questions for the library author.

Resource ownership also matters when adapting a push iterator to pull-style consumption. The versioned package documentation describes iter.Pull and its stop function, which callers must use when they finish before exhausting the iterator. That detail is a reminder that convenience can move a cleanup obligation without removing it.

Before publishing an interface, describe whether it can be traversed twice. A one-pass stream and a reusable collection are different promises. The tagged documentation explicitly discusses single-use iterators. A caller who assumes a second traversal restarts the data may otherwise produce an empty or partial result without understanding why.

The strongest objection is compatibility

A public library may have users on older Go versions. Requiring Go 1.23 to use a new API can impose work on those users even if the library author's own application has already upgraded. The benefits need to justify that change in the supported toolchain and language version. The release establishes availability, not permission to ignore a library's compatibility policy.

There is a maintenance objection too. If a stable callback interface is already clear, adding an iterator alongside it may create two contracts to support. Replacing it outright may break downstream callers. An internal application with one controlled toolchain has a different migration cost from a library used across many repositories.

This is why adoption should begin at a real interface problem. Find a traversal whose callers currently allocate an unnecessary result or manage incidental machinery. Compare the old and proposed contracts, including errors and cleanup. Keep the existing API when the new form saves little or shifts complexity into undocumented behavior.

Decide at the boundary

For custom software development, a useful iterator proposal names the caller operation and the supported Go version before showing new syntax. It explains early termination, repeat traversal and the lifetime of returned values. Where external work occurs, it explains cancellation separately.

No performance percentage follows from the release notes, and no claim of ecosystem-wide adoption is needed. A library can earn the change by making one important boundary easier to use correctly. Leave ordinary slice loops alone when they already express the operation clearly. Go 1.23 widened the interface choices; the engineering decision is which promise your callers can most reliably keep.