The major features that may have required a 2.0 were implemented in a backwards-compatible way, removing the utility of a Go 2.0.
Go 2.0 was basically a blank check for the future that said "We may need to break backwards compatibility in a big way". It turns out the Go team does not see the need to cash that check and there is no anticipated upcoming feature in the next several years that would require it.
The last one that I was sort of wondering about was the standard library, but the introduction of math/rand/v2 has made it clear the devs are comfortable ramping standard library packages without a Go 2. There are a number of standard libraries that I think could stand to take a v2; there aren't any that are so broken that it's worth a v2 to hard-remove them. (Except arguably syscall [1], which turns out it doesn't belong in the standard library because it can't maintain the standard library backwards compatibility and should have been in the extended standard library from the beginning, but that's been the way it is now for a long time and also doesn't rate a v2.)
(And again let me underline I'm not saying all the standard library is perfect. There is some brokenness here and there, for various definitions of "brokenness". I'm just saying it's not so broken that it's worth a v2 hard break at the language level and hard elimination of the libraries such that old code is forcibly broken and forced to update to continue on.)
The the introduction of Python 3 wasn't a mistake. The mistake was discontinuing Python 2.
Just look at how rust does it. Rust 1.0 code still works in the latest version of rustc, you just need to set the project to the Rust 2015 edition. You can even mix-and-match editions, as each crate can have a different edition. Newer versions of rustc will always support all previous editions, and breaking changes are only ever introduced when a new edition is released every 3 years. If the crate is stable, no real reason to upgrade, it will work forever. And if you do need to update a project, you can split it into multiple crates and do it incrementally.
Just imagine how much smoother the python 3 transition would have been if you could transition projects incrementally, module by module as needed.
It seems you are both saying the same thing. Had Python not introduced a line in the sand and instead continued to support Python 2 amid future updates there would have been no reason for Python 3. The Python 2 line could have kept improving instead.
Just as you say, Python could have introduced what is found in Python 3 without breaking Python 2 support. Which is the direction Go has settled on; hence why Go 2 is off the table. Go 1.0 and Go 1.23 are very different languages, but backwards version support is retained, so no need for a new major version.
The trouble with the Rust community is that it is terrible at communication. That may be why you extend a presupposition that everyone understands the meaningful difference between Rust editions and Go version directives, but I can't tell a difference beyond the frivolous like syntax used. Based on the documentation of each they seem like the exact same concept, with the exact same goals in mind. As a result, unfortunately, your point is not yet made. Perhaps you can break the cycle and describe for every day people how Rust editions are fundamentally different?
Editions allow making breaking changes to Rust without splitting the ecosystem - no hassle caused to existing code unless it opts into the new edition and its breaking changes. There's currently editions 2015, 2018, 2021, and 2024. When a new edition is introduced, it can make breaking changes such as introducing new keywords, but every previous edition remains supported forever by newer compiler versions.
The key part is that editions are configured per-library - libraries A and B might use editions 2015 and 2021, and your application could use edition 2018 and depend on those libraries, and it works.
If you wrote a library with the original 2015 edition, and never upgraded it to deal with new `async` and `await` keywords added in the 2018 edition, that's totally fine. Newer compilers will continue to compile it in its configured edition=2015 mode, where the new keywords don't exist (so your local variable named `async` still compiles), and new code written against newer editions may still use this 2015 edition library with no issue.
Editions are different from Go version directives because you use them to say "my library needs features added in this Go version", but they don't enable Go to make breaking changes to the language.
Editions can't do every kind of breaking change however - they mostly work for syntax level changes, and don't work for things like tearing out regrettable parts of the standard library.
For example, https://doc.rust-lang.org/edition-guide/rust-2021/warnings-p... - code that produced a lint warning in the 2018 edition produces a compiler error in the 2021 edition. That would be something that can't be done in a backwards compatible way without editions
Another example would be changes to the import syntax https://doc.rust-lang.org/edition-guide/rust-2018/path-chang... - the compiler will forever support the 2015 behavior in crates that use the 2015 edition, but crates using newer editions can use the newer behavior
I'd say Rust editions are more like going from Python 2.x to Python 2.y, than the 2->3 migration. The Rust standard library is still the same (and the string type is still "valid UTF-8") no matter the edition (this is why you can mix-and-match editions), the edition differences are mostly on the syntax.
> Just imagine how much smoother the python 3 transition would have been if you could transition projects incrementally, module by module as needed.
That would require manually converting strings to the correct type at each module boundary (you can't do it automatically, because on the Python 2.x side, you don't know whether or not a string has already been decoded/encoded into a specific character encoding; that is, you don't know whether a Python 2.x string should be represented by a "bytes" or a "str" on the Python 3.x side). That's made even harder by Python's dynamic typing (you can't statically look at the code and point all the places which might need manual review).
Go 2.0 was basically a blank check for the future that said "We may need to break backwards compatibility in a big way". It turns out the Go team does not see the need to cash that check and there is no anticipated upcoming feature in the next several years that would require it.
The last one that I was sort of wondering about was the standard library, but the introduction of math/rand/v2 has made it clear the devs are comfortable ramping standard library packages without a Go 2. There are a number of standard libraries that I think could stand to take a v2; there aren't any that are so broken that it's worth a v2 to hard-remove them. (Except arguably syscall [1], which turns out it doesn't belong in the standard library because it can't maintain the standard library backwards compatibility and should have been in the extended standard library from the beginning, but that's been the way it is now for a long time and also doesn't rate a v2.)
(And again let me underline I'm not saying all the standard library is perfect. There is some brokenness here and there, for various definitions of "brokenness". I'm just saying it's not so broken that it's worth a v2 hard break at the language level and hard elimination of the libraries such that old code is forcibly broken and forced to update to continue on.)
[1]: https://pkg.go.dev/syscall