Skip to content

text/template: index should return nil instead of index out of range error #14751

@bep

Description

@bep

Please answer these questions before submitting your issue. Thanks!

  1. What version of Go are you using (go version)?
go version go1.6 darwin/amd64
  1. What operating system and processor architecture are you using (go env)?
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/bep/go"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GO15VENDOREXPERIMENT="1"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fno-common"
CXX="clang++"
CGO_ENABLED="1"
  1. What did you do?
package main

import (
    "bytes"
    "fmt"
    "html/template"
)

func main() {
    var data struct {
        A map[string]interface{}
        B []interface{}
    }

    t, err := template.New("a").Parse(`{{ with index .A "a" }}{{ else }}Not Found{{ end }}`)

    if err != nil {
        fmt.Println(err)
        return
    }
    var b bytes.Buffer
    err = t.Execute(&b, data)

    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println("A:", b.String())
    b.Reset()

    t, err = template.New("b").Parse(`{{ with index .B 0}}{{ else }}Not Found{{ end }}`)

    if err != nil {
        fmt.Println(err)
        return
    }

    err = t.Execute(&b, data)

    if err != nil {
        fmt.Println(err)
        return
    }

    fmt.Println("B:", b.String())

}
  1. What did you expect to see?
A: Not Found
B: Not Found
  1. What did you see instead?
A: Not Found
template: b:1:8: executing "b" at <index .B 0>: error calling index: index out of range: 0

I understand why I get this error and I expect arguments ala "you can fix this by doing proper length checking of the slice. Or you can provide your own implementation of the index template func".

Sure. But it isn't always obvious in a template if I interact with a map or a slice, and with DRY in mind, I would say that range checking should be done in one place: Inside the index template func.

And the answer to the question "is this really a problem?":

I'm one of the maintainers in Hugo, probably the project with the largest amount of end-user-provided Go templates, and the number one most frequent support question by a large margin, is: "How do I access that parameter/argument/data in a safe way?"

gohugoio/hugo#1949

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeNeedsDecisionFeedback is required from experts, contributors, and/or the community before a change can be made.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions