Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12290] ファイルの概要

このコミットは、Go言語の公式ドキュメントに「The Laws of Reflection」という新しい記事を追加するものです。具体的には、以下のファイルが変更されています。

  • doc/Makefile: 新しい記事のHTMLファイルをビルドプロセスに含めるための変更。
  • doc/articles/laws_of_reflection.html: 「The Laws of Reflection」記事の最終的なHTML出力ファイル。
  • doc/articles/laws_of_reflection.tmpl: 上記HTMLファイルの生成元となるテンプレートファイル。
  • doc/docs.html: Go言語のドキュメントインデックスページに、新しい記事へのリンクを追加。
  • doc/progs/interface.go: 記事内で使用されるGoコードの例。Goのインターフェースの基本的な概念を示す。
  • doc/progs/interface2.go: 記事内で使用されるGoコードの例。Goのリフレクションの具体的な使用方法を示す。
  • src/pkg/reflect/type.go: reflectパッケージのドキュメントコメントを更新し、新しい記事へのリンクを追加。

コミット

commit 6652b0b86639a6a59e038bcb85b18fd1c1f25a95
Author: Johan Euphrosine <proppy@google.com>
Date:   Thu Mar 1 10:05:51 2012 +1100

    doc: add The Laws of Reflection article
    
    Originally published on The Go Programming Language Blog, September 6, 2011.
    
    http://blog.golang.org/2011/09/laws-of-reflection.html
    
    Update #2547
    
    R=golang-dev, r, adg
    CC=golang-dev
    https://golang.org/cl/5689054

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/6652b0b86639a6a59e038bcb85b18fd1c1f25a95

元コミット内容

このコミットは、Go言語の公式ドキュメントに「The Laws of Reflection」という記事を追加するものです。この記事は元々2011年9月6日にGoプログラミング言語の公式ブログで公開されたもので、Goにおけるリフレクションの基本原則について解説しています。

変更の背景

Go言語は静的型付け言語でありながら、reflectパッケージを通じて実行時の型情報の検査や操作を可能にする「リフレクション」の機能を提供しています。しかし、リフレクションは強力である反面、その動作原理や適切な使用方法について誤解が生じやすい側面がありました。

このコミットの背景には、Goコミュニティがリフレクションをより深く理解し、正しく活用できるようにするための公式な解説が必要であるという認識がありました。元々ブログ記事として公開されていた内容を公式ドキュメントに組み込むことで、Go言語の学習者や開発者がリフレクションに関する正確で詳細な情報を容易に参照できるようになります。これにより、リフレクションの誤用を防ぎ、より堅牢で効率的なGoプログラムの記述を促進することが目的です。

前提知識の解説

Go言語の型システム

Goは静的型付け言語です。これは、すべての変数がコンパイル時に厳密に一つの型を持つことを意味します。例えば、intfloat32*MyType[]byteなどです。

type MyInt int

var i int
var j MyInt

この例では、iint型、jMyInt型を持ちます。これらは異なる静的型であり、たとえ基底型が同じであっても、型変換なしに相互に代入することはできません。

インターフェース

Goのインターフェースは、メソッドの集合を定義する型です。インターフェース型の変数は、そのインターフェースが定義するすべてのメソッドを実装する任意の具象型(非インターフェース型)の値を格納できます。

例えば、io.ReaderインターフェースはReadメソッドを定義します。

type Reader interface {
    Read(p []byte) (n int, err error)
}

このReadメソッドを実装する任意の型はio.Readerインターフェースを実装しているとみなされます。

var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)

rがどのような具象値を保持していても、rの静的型は常にio.Readerです。Goのインターフェースは動的型付けと誤解されがちですが、実際には静的型付けであり、インターフェース変数が保持する値は常にそのインターフェースを満たします。

インターフェースの内部表現

Goのインターフェース変数は、内部的に「値」と「型記述子」のペアを格納しています。

  • : インターフェースを実装する基底の具象データ項目。
  • 型記述子: その具象項目の完全な型情報。

例えば、var r io.Reader; r = ttytty*os.File型)の場合、r(tty, *os.File)というペアを保持します。インターフェースの静的型(例: io.Reader)は、そのインターフェース変数を通じて呼び出せるメソッドを決定しますが、内部の具象値はより多くの型情報を持っています。このため、型アサーション(例: r.(io.Writer))によって、内部の値が別のインターフェースも実装しているかを確認し、その型に変換することが可能です。

重要な点として、インターフェース内部のペアは常に(値, 具象型)の形式であり、(値, インターフェース型)の形式にはなりません。インターフェースはインターフェース値を保持しません。

技術的詳細

「The Laws of Reflection」記事では、Goのリフレクションを理解するための3つの法則が提示されています。

第1の法則: リフレクションはインターフェース値からリフレクションオブジェクトへ移行する

リフレクションの基本的な目的は、インターフェース変数に格納されている型と値のペアを検査することです。reflectパッケージには、この目的のためにreflect.Typereflect.Valueという2つの主要な型があります。

  • reflect.TypeOf(i interface{}) Type: インターフェース値iからreflect.Typeを取得します。
  • reflect.ValueOf(i interface{}) Value: インターフェース値iからreflect.Valueを取得します。

reflect.TypeOf(x)のように具象値を渡した場合でも、Goは自動的にその値を空のインターフェース(interface{})にラップしてから関数に渡します。reflect.Typereflect.Valueは、それぞれが持つメソッド(例: Type()Kind()Int()Float()など)を通じて、元の値の型情報や実際の値にアクセスすることを可能にします。

Kind()メソッドは、reflect.Intreflect.Float64reflect.Sliceなど、基底の型(underlying type)を返します。これは、ユーザー定義型(例: type MyInt int)の場合でも、その基底型(int)を返します。一方、Type()メソッドは、ユーザー定義型を含む完全な静的型情報を提供します。

第2の法則: リフレクションオブジェクトからインターフェース値へ移行する

リフレクションは、その逆の操作も可能です。reflect.Valueから元のインターフェース値を取り出すには、Interface()メソッドを使用します。

func (v Value) Interface() interface{}

このメソッドは、reflect.Valueが保持する型と値の情報を再びインターフェース表現にパックし、interface{}型の結果を返します。これにより、fmt.Println(v.Interface())のように、リフレクションオブジェクトから取り出した値をfmtパッケージの関数に直接渡して、その具象値として扱うことができます。

要するに、Interface()メソッドはValueOf()関数の逆の操作であり、その結果は常に静的型interface{}となります。

第3の法則: リフレクションオブジェクトを変更するには、その値が「セッタブル(settable)」でなければならない

この法則は最も複雑ですが、リフレクションを通じて値を変更する上で非常に重要です。reflect.ValueSetメソッド(例: SetFloatSetInt)を呼び出すには、そのreflect.Valueが「セッタブル」である必要があります。セッタブルでないreflect.Valueに対してSetメソッドを呼び出すと、パニックが発生します。

CanSet()メソッドは、reflect.Valueがセッタブルであるかどうかを報告します。

var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet()) // false

上記の例でvがセッタブルでないのは、reflect.ValueOf(x)xの「コピー」が渡されるためです。リフレクションオブジェクトが元のストレージを直接変更できるようにするには、そのリフレクションオブジェクトが元の項目の「アドレス」を保持している必要があります。これは、関数に値を渡す際に、値のコピーではなくポインタを渡す必要があるのと同様の考え方です。

値を変更可能にするには、reflect.ValueOfに値へのポインタを渡します。

var x float64 = 3.4
p := reflect.ValueOf(&x) // xのアドレスを渡す
fmt.Println("type of p:", p.Type())       // *float64
fmt.Println("settability of p:", p.CanSet()) // false (p自体はポインタなのでセッタブルではない)

v := p.Elem() // ポインタを間接参照して、ポインタが指す値のreflect.Valueを取得
fmt.Println("settability of v:", v.CanSet()) // true (vはxの値を表し、セッタブル)

v.SetFloat(7.1) // xの値を変更できる
fmt.Println(x) // 7.1

構造体のフィールドを変更する場合も同様です。構造体のアドレスからreflect.Valueを作成し、Elem()メソッドで間接参照した後に、Field()メソッドで個々のフィールドのreflect.Valueを取得します。構造体のエクスポートされたフィールド(大文字で始まるフィールド)のみがセッタブルです。

コアとなるコードの変更箇所

このコミットの主要な変更は、Go言語の公式ドキュメントに新しい記事「The Laws of Reflection」を追加することです。

  1. doc/articles/laws_of_reflection.htmldoc/articles/laws_of_reflection.tmpl の追加:
    • これらは記事のコンテンツを構成する新しいファイルです。.tmplファイルはテンプレートであり、そこから最終的な.htmlファイルが生成されます。
  2. doc/Makefile の更新:
    • HTML変数にarticles/laws_of_reflection.htmlが追加され、ドキュメントのビルドプロセスでこの新しい記事がHTMLとして生成されるように設定されています。
  3. doc/docs.html の更新:
    • ドキュメントのインデックスページに、新しい記事「The Laws of Reflection」へのリンクが追加されました。これにより、ユーザーが公式ドキュメントから直接記事にアクセスできるようになります。
    • 変更前: <li><a href="http://blog.golang.org/2011/09/laws-of-reflection.html">The Laws of Reflection</a> - the fundamentals of the <a href="/pkg/reflect/">reflect</a> package.</li>
    • 変更後: <li><a href="/doc/articles/laws_of_reflection.html">The Laws of Reflection</a> - the fundamentals of the <a href="/pkg/reflect/">reflect</a> package.</li>
    • リンクがブログ記事のURLから、Goドキュメント内の新しいパスに変更されています。
  4. doc/progs/interface.godoc/progs/interface2.go の追加:
    • これらのファイルは、記事内でGoのリフレクションとインターフェースの概念を説明するために使用されるコード例を含んでいます。記事のHTMLファイル内で{{code "..."}}ディレクティブを通じてこれらのコードスニペットが埋め込まれます。
  5. src/pkg/reflect/type.go の更新:
    • reflectパッケージのドキュメントコメントが更新され、リフレクションの入門として新しい記事へのリンクが追加されました。
    • 変更前: // http://blog.golang.org/2011/09/laws-of-reflection.html
    • 変更後: // http://golang.org/doc/articles/laws_of_reflection.html

コアとなるコードの解説

このコミットの核心は、Goのリフレクションに関する包括的な解説を公式ドキュメントに統合することです。

  • doc/articles/laws_of_reflection.tmpl: このファイルは、記事の構造とコンテンツを定義するテンプレートです。Goのドキュメント生成ツールによって処理され、最終的なHTMLファイルが生成されます。このテンプレートには、Goのインターフェースとリフレクションの概念を説明するためのテキスト、コード例の埋め込み指示({{code "..."}})、およびHTMLマークアップが含まれています。特に、Goの型システム、インターフェースの内部表現、そしてリフレクションの3つの法則(インターフェース値からリフレクションオブジェクトへ、リフレクションオブジェクトからインターフェース値へ、そしてセッタブルな値の変更)が詳細に解説されています。
  • doc/progs/interface.go および doc/progs/interface2.go: これらのGoファイルは、laws_of_reflection.tmpl内で参照される具体的なコード例を提供します。
    • interface.goは、Goの静的型付け、ユーザー定義型、およびio.Readerio.Writerのような基本的なインターフェースの概念を説明するために使用されます。インターフェース変数が具象値とその型情報をどのように保持するかを示す例も含まれています。
    • interface2.goは、reflectパッケージの具体的な使用方法に焦点を当てています。reflect.TypeOfreflect.ValueOfreflect.ValueType()Kind()Interface()メソッドの使用例、そして特に「セッタブル」の概念と、ポインタを介してリフレクションで値を変更する方法が示されています。構造体のフィールドをリフレクションで操作する例も含まれており、エクスポートされたフィールドのみが変更可能であるという重要な制約も示されています。
  • doc/docs.htmlsrc/pkg/reflect/type.go のリンク更新: これらの変更は、新しい記事へのアクセスポイントを提供し、reflectパッケージの公式ドキュメントから直接参照できるようにすることで、ユーザーがリフレクションに関する情報を簡単に見つけられるようにします。これにより、Goのリフレクションに関する公式かつ信頼できる情報源が確立されます。

これらの変更により、Goのリフレクションという複雑なトピックが、公式ドキュメント内で体系的かつ詳細に解説され、開発者にとってより理解しやすいものとなっています。

関連リンク

参考にした情報源リンク

  • The Laws of Reflection - The Go Programming Language Blog: http://blog.golang.org/2011/09/laws-of-reflection.html
  • GitHub Commit: 6652b0b86639a6a59e038bcb85b18fd1c1f25a95: https://github.com/golang/go/commit/6652b0b86639a6a59e038bcb85b18fd1c1f25a95
  • Web Search Summary for "The Laws of Reflection": (Previous tool output)
    • Go's reflection builds on its statically typed system, where every variable has a fixed type at compile time, and interfaces, which store a concrete value and its type descriptor. The three laws of reflection are: 1) Reflection goes from an interface value to a reflection object ( reflect.Type or reflect.Value ). 2) Reflection goes from a reflection object back to an interface value using the Interface() method. 3) To modify a reflection object, the reflect.Value must be "settable," meaning it holds the address of the original item, not a copy. Settability is crucial for modifying values via reflection, requiring a pointer to the value.I have provided the detailed explanation as requested.