Fixing for Loops in Go 1.22

This page summarizes the projects mentioned and recommended in the original post on news.ycombinator.com

Our great sponsors
  • WorkOS - The modern identity platform for B2B SaaS
  • InfluxDB - Power Real-Time Data Analytics at Scale
  • SaaSHub - Software Alternatives and Reviews
  • go

    The Go programming language

  • Most commonly no impact. It can require an additional heap allocation per iteration if taking the address or capturing in a closure, but even in those cases escape analysis may be able to determine that the value can remain on the stack because it will not remain referenced longer than the current loop iteration. If that happens then this change has no impact.

    I'm not sure how thorough Go's escape analysis is, but nearly all programs that capture the loop variable in a closure and are not buggy right now could be shown to have that closure not escape by a sufficient thorough escape analysis. On the other hand for existing buggy programs, then perf hit is the same as assigning a variable and capturing that (the normal fix for the bug).

    Google saw no statistically significant change in their benchmarks or internal applications.

    https://github.com/golang/go/wiki/LoopvarExperiment#will-the...

  • kratos

  • I managed to track down the original code containing these snippets using the GitHub code search tool:

    https://github.com/adobe/kratos/blob/93246f92d53feba73743dbf...

    https://github.com/StalkR/goircbot/blob/6081ed5d1d74f01767d7...

    The difference is that in one case, informer is an interface, so the method call resolves informer.Run immediately and there's no issue. In the other case, a is a struct Alarm, and gets copied by value, and the Monitor method takes a pointer receiver. So my original intuition was right, the compiler is essential translating

        go a.Monitor(b)

  • WorkOS

    The modern identity platform for B2B SaaS. The APIs are flexible and easy-to-use, supporting authentication, user identity, and complex enterprise features like SSO and SCIM provisioning.

    WorkOS logo
  • goircbot

    Go IRC Bot

  • I managed to track down the original code containing these snippets using the GitHub code search tool:

    https://github.com/adobe/kratos/blob/93246f92d53feba73743dbf...

    https://github.com/StalkR/goircbot/blob/6081ed5d1d74f01767d7...

    The difference is that in one case, informer is an interface, so the method call resolves informer.Run immediately and there's no issue. In the other case, a is a struct Alarm, and gets copied by value, and the Monitor method takes a pointer receiver. So my original intuition was right, the compiler is essential translating

        go a.Monitor(b)

  • gofor

    Go For Range Copy checks if code uses range assigned variable for editing purposes

  • babili

    :scissors: An ES6+ aware minifier based on the Babel toolchain (beta)

  • The key is that the scoping happens for each iteration, not around the entire loop. That detail is nonobvious, given how many other languages have gotten it wrong, but I wouldn’t say it’s wild.

    (If you’re curious how Babel deals with the more complicated cases of continue, break, labelled break, and return, try it out at https://babeljs.io/repl.)

  • 360blockscope

    Block scoping in python (ironically)

  • Yeah loops don't get their own scopes (unless you add one using this thing I made as a joke: https://github.com/davisyoshida/360blockscope)

  • session-scheduler

    A web app for scheduling unconference-style sessions

  • This is the actual code that caused me to write the ticket above (be warned, I wouldn't consider it amazing code; my first foray into writing a web app as a side project, just trying to get something that works):

    https://github.com/gwd/session-scheduler/blob/master/handle_...

    Basically, I have several pages I'm rendering, which have common prerequisites regarding checks, and common handling processes (passing some sanitized data to a template). The GetDisplay() functions take a structure from the "database" layer and sanitize it / process it for handing to the templates. The two GetDisplay() functions return pointers to two different types, appropriate for the template to which they will be passed; and return nil if there's an issue.

    So I have a map, `data` of type `map[string]interface{}` that I pass into the templates; and two different paths set `data["Display"]`; then at the end I want to check if either of the GetDisplay() functions returned `nil`. So naturally, the first version of the code checked `data["Display"] == nil`, which was always false, since it was implicitly checking `data["Display"] == interface{}(nil)`, but the value in case of an error would be either `DiscussionDisplay(nil)` or `*UserDisplay(nil)`.

    I mean, sure, there are other ways to structure this; I could return an error or a boolean rather than returning nil. But 1) the only reason to do that is to work around this language limitation 2) it's a "foot gun" that it's easy to fall into.

    And sure, a golang developer who'd shot themselves in the foot a few times with this would catch it during review; but I don't think a bunch of newer developers would catch it, even if they had extensive experience in other languages.

  • InfluxDB

    Power Real-Time Data Analytics at Scale. Get real-time insights from all types of time series data with InfluxDB. Ingest, query, and analyze billions of data points in real-time with unbounded cardinality.

    InfluxDB logo
NOTE: The number of mentions on this list indicates mentions on common posts plus user suggested alternatives. Hence, a higher number means a more popular project.

Suggest a related project

Related posts