This change will end up removing some mental load, at an extremely low complexity cost. Basically you define an iterator just by defining a function. No additional keyword, no state to manage, just a function.
It's probably simple, but the control flow just felt convoluted.
for key, val := range m.Range
I like that a fair bit more than
m.Range(func(key, val any) bool {
It's changes like this that will make Go less productive for users over time, IMO. Java-fication.
You can also just not import things that do crazy things with iterators. Based on the history of the community, that will actually be fairly easy. I'm yet to see anything crazy with generics get into a library that I use. I'm abundantly positive there's going to be a dozen "hey let's go do crazy things with rangefunc" libraries in the next couple of weeks, probably some will even make it to HN (with predictable comments bemoaning how complicated Go is getting even though these libraries have an expected use rate in the low dozens of people in the next few years), but the odds of them penetrating into common practice remain low. (Higher than previous attempts at such libraries, because rangefunc fixes some basic issues with them. But still low overall, I think.)
I think you could go many, many years programming Go in the next few years and not encounter any funny iterators in real code. If you just assume the iterator is doing what it looks like it should be doing and isn't doing anything funny, you're going to be 99%+ correct in the Go programming world.
Well, that's one reason why people usually run away from languages high in magic like Java/JS to something like Go: not needing a fancy IDE to make sense of what's happening with a single line of code.
With `for x, y := range m.Range`, you know you are iterating over something, and you are getting an `x` and a `y`. For the purposes of understanding the range block and what it does, it doesn't really matter what `m.Range` is, only that it allows iteration. Just like you don't care in <=1.22 land whether `m.Range` is a slice, map, or channel, because for the purposes of understanding the code, that doesn't matter.
In a vacuum I think it's fine, but I don't want to see stuff like this getting added to the language routinely. I prefer a small language.
The language maintainers need to balance a small specification or a small implementation versus the users' demand, but in most cases at least some user demands win routinely. This is especially true when the language has just one major implementation. There is a significant cost for making that implementation more complex, but there is a more significant cost for making millions of programs that rely on a single implementation more complex just to make that implementation simpler.