Skip to content

text/template: index/element switched for iter.Seq2 in range #73282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
bep opened this issue Apr 9, 2025 · 5 comments
Open

text/template: index/element switched for iter.Seq2 in range #73282

bep opened this issue Apr 9, 2025 · 5 comments
Labels
BugReport Issues describing a possible bug in the Go implementation. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.

Comments

@bep
Copy link
Contributor

bep commented Apr 9, 2025

Go version

go1.24.2 darwin/arm64

Output of go env in your module/workspace:

AR='ar'
CC='clang'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='clang++'
GCCGO='gccgo'
GO111MODULE=''
GOARCH='arm64'
GOARM64='v8.0'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/Users/bep/Library/Caches/go-build'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/bep/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch arm64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/_g/j3j21hts4fn7__h04w2x8gb40000gn/T/go-build279183488=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='arm64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD='/Users/bep/dev/go/bep/temp/go.mod'
GOMODCACHE='/Users/bep/dev/gomod_cache'
GONOPROXY=''
GONOSUMDB=''
GOOS='darwin'
GOPATH='/Users/bep/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org'
GOROOT='/usr/local/go'
GOSUMDB='sum.golang.org'
GOTELEMETRY='on'
GOTELEMETRYDIR='/Users/bep/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/go/pkg/tool/darwin_arm64'
GOVCS=''
GOVERSION='go1.24.2'
GOWORK=''
PKG_CONFIG='pkg-config'

What did you do?

package main

import (
	"bytes"
	"fmt"
	"iter"
	"log"
	"slices"
	"strings"
	"text/template"
)

type MyStrings []string

func (c MyStrings) Backward() iter.Seq2[int, string] {
	return slices.Backward(c)
}

func main() {
	data := map[string]interface{}{
		"abc": MyStrings{"a", "b", "c"},
	}
	tpl := `
Range: {{ range .abc }}{{ . }}{{ end }}
Range $i, $e: {{ range $i, $e := .abc }}{{ $e }}{{ end }}
Range $i, $e dot: {{ range $i, $e := .abc }}{{ . }}{{ end }}
Range $e dot: {{ range $e := .abc }}{{ . }}{{ end }}
Backward: {{ range .abc.Backward }}{{ . }}{{ end }}
Backward $i, $e: {{ range  $i, $e := .abc.Backward }}{{ $e }}{{ end }}
Backward $i, $e dot: {{ range  $i, $e := .abc.Backward }}{{ . }}{{ end }}
Backward $e dot: {{ range $e := .abc.Backward }}{{ . }}{{ end }}
`

	var buf bytes.Buffer
	tmpl, err := template.New("").Parse(tpl)
	if err != nil {
		log.Fatal(err)
	}

	if err := tmpl.Execute(&buf, data); err != nil {
		log.Fatal(err)
	}
	result := strings.TrimSpace(buf.String())
	fmt.Println(result)
}

What did you see happen?

Range: abc
Range $i, $e: abc
Range $i, $e dot: abc
Range $e dot: abc
Backward: 210
Backward $i, $e: cba
Backward $i, $e dot: cba
Backward $e dot: 210

What did you expect to see?

Range: abc
Range $i, $e: abc
Range $i, $e dot: abc
Range $e dot: abc
Backward: cba
Backward $i, $e: cba
Backward $i, $e dot: cba
Backward $e dot: cba
@gabyhelp gabyhelp added the BugReport Issues describing a possible bug in the Go implementation. label Apr 9, 2025
@qiulaidongfeng
Copy link
Member

Existing implementation conforms to the specification
See https://go.dev/ref/spec#For_statements

Range expression                                       1st value                2nd value

function, 2 values  f  func(func(K, V) bool)           key      k  K            v          V

@bep
Copy link
Contributor Author

bep commented Apr 9, 2025

@qiulaidongfeng this issue is about the range func in Go Templates. If you apply the Go Language spec to Go Templates, you will find many discrepancies.

Some additional arguments to my initial post:

  • A range over a map/slice or a range over a basic iterator of that same map/slice should behave the same. This is how it works for regular Go, and anything else would be a big surprise.
  • This difference in behaviour requires that the end user knows/cares about the difference between a slice/map and an iterator, which becomes especially problematic in a more dynamically typed scripting language.
  • It also means that it's not possible to replace slices/maps with a struct without breaking the API, which is incredibly unfortunate.

@dmitshur
Copy link
Member

dmitshur commented Apr 9, 2025

CC @robpike, @mvdan.

@dmitshur dmitshur added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Apr 9, 2025
@qiulaidongfeng
Copy link
Member

See https://go.dev/doc/go1.24#minor_library_changes

Templates now support range-over-func and range-over-int.

not to support other new semantics.
I think the description in the go specification is the definition of range-over-func.

By the way, it has been released by go1.24, so, according to https://go.dev/doc/go1compat, for backward compatibility, we should not change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
BugReport Issues describing a possible bug in the Go implementation. NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

4 participants