Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Presence of unrelated method in interface breaks text/template?

Playground link: http://play.golang.org/p/Ebf5AuJlcP

type Foo interface {}

type Bar interface {
    ThisIsABar()
}

// Person implements both Foo and Bar
type Person struct {
    Name string
}

func (p Person) ThisIsABar() {}

type FooContext struct {
    Something   Foo
}

type BarContext struct {
    Something   Bar
}

func main() {
    t := template.Must(template.New("test").Parse("{{ .Something.Name }}\n"))

    // This works fine.
    if err := t.Execute(os.Stdout, FooContext{Person{"Timmy"}}); err != nil {
        fmt.Printf("Error: %s\n", err)
    }

    // With BarContext, containing the exact same Person but wrapped in a
    // different interface, it errors out.
    if err := t.Execute(os.Stdout, BarContext{Person{"Timmy"}}); err != nil {
        fmt.Printf("Error: %s\n", err)
    }
}

When I render a template (via the text/template package) containing {{ .Something.Name }}, I can go through interface Foo which contains no methods, and it works fine. But if I go through interface Bar instead, I get:

executing "test" at <.Something.Name>: can't evaluate field Name in type main.Bar

Why does the presence of an unrelated method on the interface, that isn't even used, affect the rendering of the template?

like image 231
Thomas Avatar asked Oct 30 '13 21:10

Thomas


1 Answers

text/template is special casing interface{}, so called functions can have return type interface{}, etc. Adding a method to your interface means that detection no longer triggers.

http://golang.org/src/pkg/text/template/exec.go#L323

323     for _, cmd := range pipe.Cmds {
324         value = s.evalCommand(dot, cmd, value) // previous value is this one's final arg.
325         // If the object has type interface{}, dig down one level to the thing inside.
326         if value.Kind() == reflect.Interface && value.Type().NumMethod() == 0 {
327             value = reflect.ValueOf(value.Interface()) // lovely!
328         }
329     }

BarContext.Something is a Bar (an interface). A Bar has no field Name. If you want to use an interface there, you'll need to provide the data via a method that is part of the interface.

like image 98
Tv_ Avatar answered Nov 17 '22 10:11

Tv_