> To ensure backwards compatibility with existing code, the new semantics will only apply in packages contained in modules that declare go 1.22 or later in their go.mod files. ... It is also possible to use //go:build lines to control the decision on a per-file basis.
Doesn't that mean that all code written so far can't take up newer versions of the Go compiler for any other reason like new features/bugfixes/optimizations/etc without a full audit of codepaths involving for loops?
No, the version declared in go.mod is different than the version of the toolchain used to compile the project. If you declare an older version even new toolchains will act like the previous versions.
Without /:go:build tags, you can just define 1.21 as your Go version in go.mod to opt out of new features while getting other benefits of the new compiler
No I don't think so; any old working code will be using the x := x workaround, which will keep working when going to this version with the changed loop mechanics. What may happen is a form of... some adage, I forgot the name, where code accidentally relies on the old behaviour and breaks when that old behaviour is no longer there.
(that same adage applies to e.g. browser manufacturers having to implement bugs to not break certain websites)
I don't know why you're being down voted, but it is actually breaking the Go1 compat promise. Which says:
It is intended that programs written to the Go 1 specification will continue to compile and run correctly, unchanged, over the lifetime of that specification. At some indefinite point, a Go 2 specification may arise, but until that time, Go programs that work today should continue to work even as future "point" releases of Go 1 arise (Go 1.1, Go 1.2, etc.).
I upvoted the question to offset one of the downs because I agree it's a fair question. However I would guess the downvotes are because TFA addressed this issue directly and comprehensively, so it's a clear "I didn't read the article" indicator :-) Possibly also because the downvoters can't imagine a scenario where this would be desirable behavior (i.e. it's always a bug)
Yeah its fair, I didn't closely read that section. Although, I'm not entirely convinced the approach is safe, maybe its worth it to fix such a common pitfall.
> The end of the document warns, “[It] is impossible to guarantee that no future change will break any program.” Then it lays out a number of reasons why programs might still break.
> For example, it makes sense that if your program depends on a buggy behavior and we fix the bug, your program will break. But we try very hard to break as little as possible and keep Go boring.
> In a previous blog post they basically said they will never make a Go 2
No, they didn't say that, they said it wouldn't be backwards-incompatible with Go 1. Relevant quote:
> [...] when should we expect the Go 2 specification that breaks old Go 1 programs?
> The answer is never. Go 2, in the sense of breaking with the past and no longer compiling old programs, is never going to happen. Go 2 in the sense of being the major revision of Go 1 we started toward in 2017 has already happened.
This has all been addressed in the proposal. The research was done and this change will impact so few projects that it’s worth making a technical exception to the compatibility promise to fix a real design flaw.
I assume that if compiling with 1.22 or later, you still get all the benefits from that version like other new features, bug fixes or perf improvements, just not this particular change.
No, it doesn't break the promise; "Go programs that work today should continue to work even as future "point" releases of Go 1 arise (Go 1.1, Go 1.2, etc.)."
You can install Go 1.22 and your program will compile and run as-is. That's the promise. If however you opt-in to the changed for loop behaviour by adjusting your go.mod, the onus is on you to update your program accordingly.
It's only a backwards incompatible change if the developer makes a backwards incompatible change by updating the configured target version.
(I'm aware I'm probably being pedantic here, I understand the language used seems to imply you can just set it to v1.22 and it works but it's a bit more specific)
To add to the other comments, in the run-up to go1.21 they talked about how they’d analysed a very large corpus of Go code to see what would be affected, and it was a very very small number.
I remember thinking that the number of people who have created inadvertent bugs due to this design (myself included) would be significantly greater than the number of people affected by the fix.
The original proposal for this change went into great detail about the research they did into existing uses of this syntax. In my memory, they found vanishingly few cases in the Google codebase or GitHub code where the change would violate the expected behavior. The decision to break the backwards compatibility here came only after determining how few codebases would be affected and developing a mechanism in Go itself (the version specification in go.mod) to require actively modifying the code to build with the new behavior.
Python has the same problem (to the extent that it's actually a problem, which you might or might not agree with), and this is the #1 reason they won't change it.
Yeah I don't think it's so much "we explicitly rely on this behavior, how dare you change this" as "somewhere in our mountains of maintenance-mode code that haven't seen the sun shine through an editor window in years, this behavior cancels out another bug that we never noticed". Tooling should be able to detect when code relies on this, but it's still gonna cost some non-zero amount of developer effort to touch ancient code and safely roll out a new version if it needs to be actively addressed.
If you have tests and they break with GOEXPERIMENT=loopvar, then there is a new tool that will tell you exactly which loop is causing the breakage. That's a post for a few weeks from now.
Neither can I, but there may be cases of code accidentally relying on it - there's an adage that I forgot the name of that says just that, and I think compiler manufacturers are the most aware of that adage.
They do. Go has avoided most of the pitfalls that other language eco-systems have fallen for over the years (backwards compatibility issues, soft forks masquerading as language improvements, re-booting the whole language under the same name, aggressively pushing down on other languages etc). They've done remarkably well in those respects, and should deserve huge credit for it.
Yes, the loopvar change will break some programs, and hence the compatibility promise. But the Go team argues that the change will fix much more programs than it will break [1].
This makes me wonder, though, what guarantees that a similar breaking change won't ever happen again in the future? If any change with #(programs fixed) >> #(programs broken) is accepted, we might as well remove the compatibility promise page [2].