goのtext/templateでprivateなattributeを出力しようとするとエラーになるという話
goのtext/templateでprivateなattributeを出力しようとすると、エラーになるという話を知らなかった。
全部public
例えば、以下はOK
type User struct { Name string Message string } type V map[string]interface{} func main() { const tmpl = `{{.user.Name}}: {{.user.Message}}` tpl := template.Must(template.New("mytemplate").Parse(tmpl)) user := User{Name: "foo", Message: "hello world"} if err := tpl.Execute(os.Stdout, V{"user": user}); err != nil { fmt.Println(err) } }
一部privateに
これを以下の様にする
@@ -7,19 +7,20 @@ ) type User struct { - Name string + name string Message string } type V map[string]interface{} func main() { - const tmpl = `{{.user.Name}}: {{.user.Message}}` + const tmpl = `{{.user.name}}: {{.user.Message}}` tpl := template.Must(template.New("mytemplate").Parse(tmpl)) - user := User{Name: "foo", Message: "hello world"} + user := User{name: "foo", Message: "hello world"}
User.Name
を User.name
にした。
するとエラーになる。
$ go run xxx.go template: mytemplate:1:7: executing "mytemplate" at <.user.name>: name is an unexported field of struct type interface {}
実際のところドキュメントに書いてあった
template - The Go Programming Language
Template is the representation of a parsed template. The *parse.Tree field is exported only for use by html/template and should be treated as unexported by all other clie
exported/unexportedチェックしているところ
単純に、template部分は別パッケージという扱いで非公開のメンバーにアクセスしようとしたからエラーということっぽい。 reflectionを使っているしruntime時にしかわからないのでruntime error。
text/template/exec.go の以下の部分で
func (s *state) evalField(dot reflect.Value, fieldName string, node parse.Node, args []parse.Node, final, receiver reflect.Value) reflect.Value { // snip... switch receiver.Kind() { case reflect.Struct: tField, ok := receiver.Type().FieldByName(fieldName) if ok { // snip... if tField.PkgPath != "" { // field is unexported s.errorf("%s is an unexported field of struct type %s", fieldName, typ) }
tFieldは StructField型
reflect/type.go
// A StructField describes a single field in a struct. type StructField struct { // Name is the field name. Name string // PkgPath is the package path that qualifies a lower case (unexported) // field name. It is empty for upper case (exported) field names. // See https://golang.org/ref/spec#Uniqueness_of_identifiers PkgPath string Type Type // field type Tag StructTag // field tag string Offset uintptr // offset within struct, in bytes Index []int // index sequence for Type.FieldByIndex Anonymous bool // is an embedded field }
reflect/type.go のあたりでexportされていないfieldに対してはpkgPathを設定しているという感じになっているので。確かにexportedかどうかの判別に使えそう。
func (t *structType) Field(i int) (f StructField) { // snip.. if !p.name.isExported() { // Fields never have an import path in their name. f.PkgPath = t.pkgPath.name() } }
goを1.7に上げてから、gocodeが動かなくなったりもしました
go1.6.2 -> go1.7でgocodeが動作しない
手元のmacの環境のgoを1.7に上げたところ、gocodeが上手く動作しなくなった。 (macportsを使っている)
$ sudo port selfupdate $ sudo port upgrade go $ go version go version go1.7 darwin/amd64
以下の様な感じの出力を返す様になってしまった。
$ cat xxx.go | gocode -debug autocomplete /tmp/xxx/xxx.go c89 Found 1 candidates: PANIC PANIC PANIC
対応
以下を参考にして、gocodeをcloseした後、インストールしなおしたら直った。
$ gocode close $ go get -u github.com/nsf/gocode
上手く動作している。
$ cat xxx.go | gocode -debug autocomplete /tmp/xxx/xxx.go c89 Found 7 candidates: func Command(name string, arg ...string) *exec.Cmd func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd func LookPath(file string) (string, error) type Cmd struct type Error struct type ExitError struct var ErrNotFound error
memo
/tmp/xxx/xxx.go
package main import ( "os/exec" ) // cancel by context func main(){ cmd := exec.//|ここにカーソル }