Go 模板中的零指针计算…为什么?有更好的策略吗?

Go 模板中的零指针计算…为什么?有更好的策略吗?,go,go-templates,Go,Go Templates,我正在尝试包装html/模板,因此我保证除了要呈现的数据之外,我的模板中还包含某些数据(例如会话数据)。然而,我目前的方法……有缺陷。下面是一个简化的示例: package main import "fmt" import "os" import "html/template" func main() { // Passing nil directly to Execute doesn't render anything for missing struct fields f

我正在尝试包装html/模板,因此我保证除了要呈现的数据之外,我的模板中还包含某些数据(例如会话数据)。然而,我目前的方法……有缺陷。下面是一个简化的示例:

package main

import "fmt"
import "os"
import "html/template"

func main() {
    // Passing nil directly to Execute doesn't render anything for missing struct fields
    fmt.Print("Directly rendering nil\n")
    tmpl, err := template.New("master").Parse("Foo is: {{.Data.Foo}}")
    if err != nil {
        fmt.Printf(err.Error())
        return
    }

    err = tmpl.Execute(os.Stdout, nil)
    if err != nil {
        fmt.Printf(err.Error())
        return
    }

    // Wrapping templates works as long as I supply data...
    fmt.Print("\nRendering Foo\n")
    render(struct {
        Foo string
    }{
        "foo",
    })

    // ...but this breaks.
    fmt.Print("\nRendering nil\n")
    render(nil)
}

func render(data interface{}) {
    allData := struct {
        Session string
        Data    interface{}
    }{
        "sessionData",
        data,
    }

    // Hardcoded template for the example - this could be any arbitrary template
    tmpl, err := template.New("master").Parse("Foo is: {{.Data.Foo}}")
    if err != nil {
        fmt.Printf(err.Error())
        return
    }

    err = tmpl.Execute(os.Stdout, allData)
    if err != nil {
        fmt.Printf(err.Error())
        return
    }
}
我得到以下输出:

Directly rendering nil
Foo is: 
Rendering Foo
Foo is: foo
Rendering nil
Foo is: template: master:1:15: executing "master" at <.Data.Foo>: nil pointer evaluating interface {}.Foo
直接渲染nil
富是:
渲染Foo
福是:福
呈现零
Foo是:template:master:1:15:在:nil指针处执行“master”求值接口{}.Foo
因此,我不太清楚首先发生了什么-为什么html/template能够处理传递的
nil
,但却不知道如何使用nil指针


其次,有没有更好的方法来解决这个问题?

您最好的办法是始终将数据设置为映射或结构,方法是将类型设置为映射或结构,或者不将nil与
接口{}
一起使用:

package main

import "fmt"
import "os"
import "text/template"

func main() {
    tmpl, err := template.New("master").Parse("{{if .Data.Foo}}Foo is: {{.Data.Foo}}{{else}}Foo is empty{{end}}")
    if err != nil {
        fmt.Printf(err.Error())
        return
    }

    err = tmpl.Execute(os.Stdout, nil)
    if err != nil {
        fmt.Printf(err.Error())
        return
    }

    fmt.Println("")

    err = tmpl.Execute(os.Stdout, struct {
        Session string
        Data    map[string]string
    }{
        "sessionData",
        nil,
    })
    if err != nil {
        fmt.Printf(err.Error())
        return
    }

    fmt.Println("")

    err = tmpl.Execute(os.Stdout, struct {
        Session string
        Data    interface{}
    }{
        "sessionData",
        map[string]string{},
    })
    if err != nil {
        fmt.Printf(err.Error())
        return
    }

    fmt.Println("")
}
播放:

至于为什么它是这样工作的,有点复杂,你必须看看代码:

当使用nil调用execute时,
reflect.ValueOf(nil)
返回一个无效值,因此evalField返回零值,结果是一个空字符串

但是,当使用有效结构调用execute时,第一个reflect.ValueOf将返回一个有效值。
.Data
命令对传递给执行的整个结构调用evalField,evalField调用FieldByIndex/FieldByName以获取“数据”字段。这不会返回无效值

接下来,当计算
.Foo
时,如果数据是一个接口或指针,函数将跟随它直到结束,如果它发现它为零,它将失败并出现此错误

当数据是映射时,间接函数不做任何事情,也不会失败


这可能是文本/模板包中的一个错误。

这可能是一个错误,如果没有,请尝试使用Go1.6,否则请尝试提交一个错误。