[インデックス 10508] ファイルの概要
このコミットは、Go言語の標準ライブラリ text/template
パッケージにおける Template
型のメソッド Template
の名称を Lookup
に変更するものです。この変更は、特に html/template
パッケージのように Template
型を埋め込む際に発生する命名の衝突や扱いにくさを解消することを目的としています。
コミット
commit e9025df7ad41d93c1c8943323db06bb49c8a16fe
Author: Rob Pike <r@golang.org>
Date: Sat Nov 26 08:32:55 2011 -0800
text/template: rename the method Template.Template to Template.Lookup
Calling it Template makes it clumsy to embed the type, which html/template
depends on.
R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/5432079
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e9025df7ad41d93c1c8943323db06bb49c8a16fe
元コミット内容
text/template
パッケージの Template
型に定義されていた、指定された名前のテンプレートを返すメソッド Template
の名前を Lookup
に変更しました。この変更に伴い、関連するテストコードも更新されています。
変更の背景
この変更の主な背景は、Go言語の構造体埋め込み(embedding)機能と、text/template
パッケージの上に構築されている html/template
パッケージの存在にあります。
Go言語では、ある構造体(型)を別の構造体に埋め込むことで、埋め込まれた型のメソッドを埋め込み先の型が直接利用できるようになります。この際、埋め込まれた型のメソッド名が、埋め込み先の型自身のメソッド名やフィールド名と衝突すると、予期せぬ挙動やコードの読みにくさを引き起こす可能性があります。
text/template
パッケージの Template
型には、Template
という名前のメソッドが存在していました。これは、Template
型のインスタンスから、そのインスタンスに関連付けられた別のテンプレート(名前で識別される)を取得するためのものでした。
一方、html/template
パッケージは text/template
パッケージを基盤として構築されており、内部で text/template.Template
型を埋め込んで利用しています。もし html/template
パッケージの Template
型が、埋め込んだ text/template.Template
型の Template
メソッドと同じ名前のメソッドやフィールドを持っていた場合、命名衝突が発生し、コードが「clumsy(扱いにくい、不格好)」になると判断されました。
この問題を回避し、将来的な拡張性やコードの明確性を保つために、Template.Template
メソッドを Template.Lookup
にリネームすることが決定されました。Lookup
という名前は、特定の名前を持つ要素を「検索する」というメソッドの機能により合致しており、命名衝突のリスクも低減されます。
前提知識の解説
Go言語のtext/template
パッケージ
text/template
パッケージは、Go言語の標準ライブラリの一部であり、テキストベースのテンプレートを生成するための機能を提供します。これは、動的にコンテンツを生成する際に非常に便利で、例えばWebページのHTML、設定ファイル、コード生成などに利用されます。
主要な概念は以下の通りです。
Template
型: テンプレートのコンパイル済み表現を表す型です。この型を通じて、テンプレートの解析、実行、関連するテンプレートの管理などが行われます。- テンプレートの定義: テンプレートは、
{{...}}
で囲まれたアクション(例:{{.Name}}
でデータ構造のフィールドにアクセス、{{range .Items}}...{{end}}
でループ処理、{{if .Condition}}...{{end}}
で条件分岐)と、プレーンテキストから構成されます。 - 名前付きテンプレート: 一つの
Template
インスタンスは、複数の名前付きテンプレートを保持できます。これにより、共通のレイアウトや部分的なテンプレートを再利用できます。例えば、{{template "header"}}
のように別のテンプレートを呼び出すことができます。
Go言語の構造体埋め込み(Embedding)
Go言語の構造体埋め込みは、他のプログラミング言語における継承に似た機能ですが、よりシンプルで柔軟な「コンポジション(合成)」のメカニズムです。ある構造体の中に、フィールド名なしで別の構造体を宣言することで、埋め込まれた構造体のフィールドやメソッドが、埋め込み先の構造体のものとして「昇格(promoted)」されます。
例:
type Base struct {
ID int
}
func (b Base) GetID() int {
return b.ID
}
type Derived struct {
Base // Base構造体を埋め込み
Name string
}
func main() {
d := Derived{Base: Base{ID: 1}, Name: "Test"}
fmt.Println(d.GetID()) // Derived型からBase型のGetIDメソッドを直接呼び出せる
}
この機能はコードの再利用性を高めますが、埋め込まれた型のメソッド名と埋め込み先の型のメソッド名が同じ場合、埋め込み先の型のメソッドが優先されます。これが、今回のコミットで問題となった「clumsy」な状況を引き起こす可能性がありました。
html/template
パッケージ
html/template
パッケージは、text/template
パッケージを基盤として構築されており、HTML出力に特化したテンプレート機能を提供します。最も重要な違いは、html/template
が自動的にコンテキストに応じたエスケープ処理を行う点です。これにより、クロスサイトスクリプティング(XSS)などのセキュリティ脆弱性を防ぐことができます。
html/template
のTemplate
型は、内部でtext/template
のTemplate
型を埋め込んでおり、text/template
の基本的な機能を利用しつつ、HTML特有のセキュリティ機能を追加しています。
技術的詳細
このコミットは、text/template
パッケージ内のTemplate
構造体のメソッドTemplate(name string) *Template
をLookup(name string) *Template
にリネームするという、比較的単純ながらも重要な変更です。
変更前:
// Template returns the template with the given name that is associated with t,
// or nil if there is no such template.
func (t *Template) Template(name string) *Template {
return t.tmpl[name]
}
変更後:
// Lookup returns the template with the given name that is associated with t,
// or nil if there is no such template.
func (t *Template) Lookup(name string) *Template {
return t.tmpl[name]
}
このリネームにより、Template
型を埋め込む他の型(特にhtml/template.Template
)が、自身のTemplate
という名前のメソッドやフィールドを持つ場合に、名前の衝突を避けることができます。
例えば、html/template
パッケージのTemplate
型が、text/template.Template
を埋め込みつつ、自身もTemplate
という名前のメソッド(例えば、テンプレート自体を返すようなメソッド)を持っていたとします。
変更前:
type HTMLTemplate struct {
*text_template.Template // text/template.Templateを埋め込み
// ...
}
// もしHTMLTemplateが独自のTemplateメソッドを持っていた場合
func (ht *HTMLTemplate) Template() *HTMLTemplate {
// ...
}
// この場合、ht.Template("name") と ht.Template() の呼び出しが曖昧になるか、
// 埋め込み先のメソッドが優先されて意図しない挙動になる可能性がある。
変更後:
type HTMLTemplate struct {
*text_template.Template // text/template.Templateを埋め込み
// ...
}
// HTMLTemplateが独自のTemplateメソッドを持っていても問題ない
func (ht *HTMLTemplate) Template() *HTMLTemplate {
// ...
}
// テンプレートのルックアップは ht.Lookup("name") と明確に呼び出せる
このように、Lookup
というより具体的な名前に変更することで、メソッドの役割が明確になり、Goの構造体埋め込みのセマンティクスとより調和するようになりました。これは、APIの設計における一貫性と将来的な保守性を向上させるための良いプラクティスです。
コアとなるコードの変更箇所
このコミットによるコードの変更は、以下の2つのファイルにわたります。
src/pkg/text/template/exec_test.go
src/pkg/text/template/template.go
src/pkg/text/template/exec_test.go
このファイルはtext/template
パッケージの実行に関するテストコードです。
変更内容は、Template.Template
メソッドの呼び出しをTemplate.Lookup
に置き換えるものです。
--- a/src/pkg/text/template/exec_test.go
+++ b/src/pkg/text/template/exec_test.go
@@ -677,7 +677,7 @@ func TestTree(t *testing.T) {
}\n \tconst expect = \"[1[2[3[4]][5[6]]][7[8[9]][10[11]]]]\"\n \t// First by looking up the template.\n-\terr = tmpl.Template(\"tree\").Execute(&b, tree)\n+\terr = tmpl.Lookup(\"tree\").Execute(&b, tree)\n \tif err != nil {\n \t\tt.Fatal(\"exec error:\", err)\n \t}\n```
### `src/pkg/text/template/template.go`
このファイルは`text/template`パッケージの主要な定義が含まれています。
変更内容は、`Template`構造体の`Template`メソッドの定義を`Lookup`にリネームするものです。
```diff
--- a/src/pkg/text/template/template.go
+++ b/src/pkg/text/template/template.go
@@ -136,9 +136,9 @@ func (t *Template) Funcs(funcMap FuncMap) *Template {
return t
}\n \n-// Template returns the template with the given name that is associated with t,\n+// Lookup returns the template with the given name that is associated with t,\n // or nil if there is no such template.\n-func (t *Template) Template(name string) *Template {\n+func (t *Template) Lookup(name string) *Template {\n \treturn t.tmpl[name]\n }\n \n```
## コアとなるコードの解説
このコミットの核心は、`src/pkg/text/template/template.go`ファイルにおける`Template`構造体のメソッドのリネームです。
変更前:
```go
func (t *Template) Template(name string) *Template {
return t.tmpl[name]
}
このメソッドは、Template
型のレシーバt
に対して呼び出され、引数name
で指定された名前のテンプレートを、内部のマップt.tmpl
から検索して返していました。メソッド名が型名と同じTemplate
であったため、html/template
のような他のパッケージがtext/template.Template
を埋め込む際に、命名衝突の可能性がありました。
変更後:
func (t *Template) Lookup(name string) *Template {
return t.tmpl[name]
}
メソッド名がLookup
に変更されたことで、その機能(名前による検索)がより明確になり、かつ型名との衝突がなくなりました。これにより、Template
型を埋め込む他の型が、自身のTemplate
という名前のメソッドやフィールドを持っていても、曖昧さなくLookup
メソッドを呼び出すことができるようになります。
src/pkg/text/template/exec_test.go
の変更は、このメソッド名のリネームに伴うテストコードの修正です。メソッド名が変わったため、テストコード内の呼び出し箇所もそれに合わせて更新されています。これは、リファクタリングにおける一般的な手順であり、変更が正しく反映され、既存の機能が損なわれていないことを確認するために不可欠です。
関連リンク
- Go言語の
text/template
パッケージのドキュメント: https://pkg.go.dev/text/template - Go言語の
html/template
パッケージのドキュメント: https://pkg.go.dev/html/template - Go言語の構造体埋め込みに関する公式ブログ記事(英語): https://go.dev/blog/go-for-java-programmers (Embeddingのセクションを参照)
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコード
- Go言語の構造体埋め込みに関する一般的な解説記事
- コミットメッセージに記載されているGoのコードレビューシステム(Gerrit)のリンク:
https://golang.org/cl/5432079
(現在はGoのGerritインスタンスはGitHubに移行しているため、直接アクセスしても情報が得られない可能性がありますが、当時の変更履歴を追うための参照情報です。)