模板不会将接口类型作为底层类型来评估字段。

huangapple go评论111阅读模式
英文:

template won't evaluate fields that are interface type as the underlying type

问题

使用golang的html/template(与text/template的行为相同)。如果我有一个结构体,其中一个成员是接口类型,我无法访问底层类型的成员(具体来说,我想访问实现接口InnerInterface的结构体上的字段,但是通过InnerInterface接口类型返回,而不是结构体类型)。

http://play.golang.org/p/ZH8wSK83oM

package main

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

type InnerInterface interface{ InnerSomeMethod() }

type MyInnerStruct struct { Title string }
func (mis MyInnerStruct)InnerSomeMethod() { fmt.Println("Just to show we're satisfying the interface") }
    
type MyOuterStruct struct { Inner InnerInterface }


func main() {

    fmt.Println("Starting")
    
    
    arg := MyOuterStruct{Inner:MyInnerStruct{Title:"test1"}}
    
    err := template.Must(template.New("testtmpl").Parse("{{.Inner.Title}}")).Execute(os.Stdout, arg)
    if err != nil { panic(err) }

}

type MyOuterStruct struct { Inner InnerInterface }更改为完全通用的接口,即type MyOuterStruct struct { Inner interface{} }可以正确渲染。这让我相信渲染引擎对interface{}进行了特殊处理。

有没有比在需要动态评估字段时都使用interface{}更好的方法?

英文:

Using golang html/template (same behavior with text/template). If I have a struct with a member that is of an interface type, I cannot access members of the underlying type (specifically trying to access fields that are on a struct that implements interface InnerInterface but is return via the InnerInterface interface type, not the struct type).

http://play.golang.org/p/ZH8wSK83oM

package main

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

type InnerInterface interface{ InnerSomeMethod() }

type MyInnerStruct struct { Title string }
func (mis MyInnerStruct)InnerSomeMethod() { fmt.Println("Just to show we're satisfying the interface") }
	
type MyOuterStruct struct { Inner InnerInterface }


func main() {

	fmt.Println("Starting")
	
	
	arg := MyOuterStruct{Inner:MyInnerStruct{Title:"test1"}}
	
	err := template.Must(template.New("testtmpl").Parse("{{.Inner.Title}}")).Execute(os.Stdout, arg)
	if err != nil { panic(err) }

}

Changing: type MyOuterStruct struct { Inner InnerInterface } to a totally generic interface, i.e. type MyOuterStruct struct { Inner interface{} } makes it render properly. This leads me to believe that interface{} is treated specially by the rendering engine.

Is there a better way to do this than to use interface{} whenever I want to be able to dynamically evaluate fields like this?

答案1

得分: 5

你说得对,interface{}在渲染引擎中的处理方式确实不同。只有interface{}类型的值会被解包,具有方法集的接口值不会被解包。我想背后的原因是,如果你有一个接口类型,你特别限制了该类型的方法集。因此,你不希望模板引擎尝试访问可能在该接口后面的成员。

这个"问题"是由exec.go中的indirect函数引起的:

func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
    for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
        if v.IsNil() {
            return v, true
        }
        if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
            break
        }
    }
    return v, false
}

这个方法被调用来获取反射值的最深层值。假设你有一个指针指向一个指针指向一个指针,这个函数将返回最后一个指针的值。对于接口值也是一样的。关键在于,一旦一个接口值有多于0个方法,间接引用就会停止。这正是你描述的行为。

既然这似乎是预期的行为,你可以在你的接口中定义一个Title() string方法,并让它返回字符串。

英文:

You're correct with saying that interface{} is handled differently by the rendering
engine. Only interface{} values are unpacked, interface values that have a method set are not.
I suppose the reasoning behind this is that if you have a interface type, you specifically limit the type to the method set. Therefore, you don't want the template engine trying to access members that may lie behind that interface.

The 'problem' is caused by the function indirect in exec.go:

func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
	for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
		if v.IsNil() {
			return v, true
		}
		if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
			break
		}
	}
	return v, false
}

This method is called to get to the deepest value of a reflected value.
Suppose you have a pointer on a pointer on a pointer, this function will get return the
last of these. The same goes for interface values. The crux is that as soon as a
interface value has more than 0 methods, the indirection stops there. Exactly the
behaviour you're describing.

As this seems to be intended behaviour, what you can do is to define a Title() string
method in your interface and let it return the string.

huangapple
  • 本文由 发表于 2013年10月24日 07:30:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/19554209.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定