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

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

このコミットは、Go言語の公式FAQドキュメントである doc/go_faq.html ファイルに対する修正です。具体的には、Go言語のインターフェースに関する説明の中で使用されているコード例の誤りを修正しています。このファイルは、Go言語に関するよくある質問とその回答をまとめたもので、Go言語の概念を理解するための重要なリソースです。

コミット

このコミットは、doc/go_faq.html 内のGo言語のインターフェースに関するコード例の誤りを修正するものです。Open メソッドのシグネチャから引数 name を削除し、Open() Reader とすることで、より正確なインターフェースの定義を示しています。

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

https://github.com/golang/go/commit/c9121507610390a6bb0b00bb3ebbf45e54c9c776

元コミット内容

commit c9121507610390a6bb0b00bb3ebbf45e54c9c776
Author: David Symonds <dsymonds@golang.org>
Date:   Wed May 22 12:28:58 2013 +1000

    doc/go_faq: fix example.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/9564044

変更の背景

この変更の背景には、Go言語の公式FAQドキュメント doc/go_faq.html に記載されていたコード例に誤りがあったことが挙げられます。Go言語のインターフェースの概念を説明するセクションにおいて、Opener インターフェースの Open メソッドの定義が Open(name) Reader となっていました。しかし、その後に続く func (t T3) Open() *os.File という具体的な型 T3Open メソッドの実装では引数がありませんでした。

Go言語において、インターフェースを実装する型は、インターフェースで定義されたすべてのメソッドを、そのシグネチャ(メソッド名、引数の型と数、戻り値の型と数)と完全に一致するように実装する必要があります。この不一致は、読者に混乱を招く可能性があり、Go言語のインターフェースの正しい理解を妨げる恐れがありました。

そのため、このコミットでは、FAQのコード例を修正し、インターフェースの定義と具体的な実装の整合性を保つことで、ドキュメントの正確性と読者の理解を向上させることを目的としています。

前提知識の解説

このコミットの変更内容を理解するためには、以下のGo言語の基本的な概念を理解しておく必要があります。

1. Go言語のインターフェース (Interfaces)

Go言語のインターフェースは、メソッドのシグネチャの集合を定義する型です。インターフェースは、そのインターフェースで定義されたすべてのメソッドを実装する任意の型によって「満たされる」ことができます。Go言語のインターフェースは、JavaやC#のような明示的な implements キーワードを必要とせず、型がインターフェースのすべてのメソッドを実装していれば、自動的にそのインターフェースを満たしていると見なされます(構造的型付け)。

インターフェースの主な目的は、異なる具体的な型が共通の振る舞いを共有できるようにすることです。これにより、柔軟で拡張性の高いコードを書くことができます。例えば、io.Reader インターフェースは Read メソッドを持つことを定義しており、ファイル、ネットワーク接続、メモリ上のバッファなど、様々なデータソースが io.Reader として扱われることができます。

インターフェースの定義例:

type MyInterface interface {
    MethodA()
    MethodB(int) string
}

2. メソッドのシグネチャ (Method Signature)

Go言語におけるメソッドのシグネチャとは、メソッドの名前、引数の型と数、そして戻り値の型と数の組み合わせを指します。インターフェースを実装する際には、メソッド名だけでなく、引数と戻り値の型と数もインターフェースの定義と完全に一致している必要があります。

例:

  • func (t T) MyMethod(): 引数なし、戻り値なし
  • func (t T) MyMethod(arg1 int) string: int 型の引数1つ、string 型の戻り値1つ

3. os.File

os.File は、Go言語の os パッケージで定義されている型で、ファイルシステム上のファイルを表します。この型は、ファイルの読み書き、クローズなどの操作を行うためのメソッドを提供します。例えば、os.Open 関数は *os.File 型の値を返します。

4. Reader インターフェース

このコミットの例では Reader というインターフェースが登場しますが、これはGo標準ライブラリの io.Reader インターフェースを指している可能性が高いです。io.Reader は、データを読み込むための単一の Read メソッドを定義する非常に一般的なインターフェースです。

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

このインターフェースは、様々な入力源(ファイル、ネットワーク接続、メモリバッファなど)からデータを統一的に読み込むための抽象化を提供します。

技術的詳細

このコミットの技術的な詳細は、Go言語のインターフェースの厳密な型チェックと、メソッドシグネチャの一致の重要性に集約されます。

元の doc/go_faq.html のコード例では、以下のように Opener インターフェースが定義されていました。

<pre>
type Opener interface {
   Open(name) Reader
}
</pre>

ここで Open(name) Reader は、Open という名前のメソッドが name という引数を取り、Reader 型の値を返すことを意図しています。しかし、Go言語のインターフェースのメソッド定義において、引数の型を省略することはできません。name が何の型であるか不明確であり、これはGoの文法として不正です。

そして、このインターフェースを実装する具体的な型 T3Open メソッドは以下のように定義されていました。

<pre>
func (t T3) Open() *os.File
</pre>

この func (t T3) Open() *os.File は、Open メソッドが引数を取らず、*os.File 型の値を返すことを示しています。

ここで問題となるのは、インターフェース OpenerOpen メソッドのシグネチャ (Open(name) Reader) と、具体的な型 T3Open メソッドのシグネチャ (Open() *os.File) が一致していない点です。

  1. 引数の不一致: インターフェースの Openname という引数を取るように見えますが、T3Open は引数を取っていません。
  2. 戻り値の型の不一致: インターフェースの OpenReader を返すように見えますが、T3Open*os.File を返しています。

Go言語では、インターフェースを実装するためには、メソッド名、引数の型と数、戻り値の型と数がすべてインターフェースの定義と完全に一致している必要があります。*os.Fileio.Reader インターフェースを満たす型であるため、戻り値の型については *os.FileReader インターフェースを満たしていれば問題ありません。しかし、引数の不一致は明確な問題です。

このコミットでは、この不一致を解消するために、インターフェースの Open メソッドの定義を Open() Reader に修正しました。

--- a/doc/go_faq.html
+++ b/doc/go_faq.html
@@ -701,7 +701,7 @@ A related example goes the other way:
 
 <pre>
 type Opener interface {
-   Open(name) Reader
+   Open() Reader
 }
 
 func (t T3) Open() *os.File

この修正により、Opener インターフェースの Open メソッドは引数を取らないことになり、T3Open メソッドのシグネチャと完全に一致するようになりました。これにより、FAQのコード例がGo言語のインターフェースのルールに則った正確なものとなり、読者の誤解を防ぐことができます。

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

diff --git a/doc/go_faq.html b/doc/go_faq.html
index 62a564b6bf..6cca842406 100644
--- a/doc/go_faq.html
+++ b/doc/go_faq.html
@@ -701,7 +701,7 @@ A related example goes the other way:
 
 <pre>
 type Opener interface {
-   Open(name) Reader
+   Open() Reader
 }
 
 func (t T3) Open() *os.File

コアとなるコードの解説

上記の diff は、doc/go_faq.html ファイルの701行目付近の変更を示しています。

  • - Open(name) Reader: 削除された行です。これは、Opener インターフェースの Open メソッドが name という引数を取るように定義されていましたが、これはGo言語の文法として不正確であり、またその後に続く具体的な実装 func (t T3) Open() *os.File とも一致していませんでした。
  • + Open() Reader: 追加された行です。Open メソッドの定義から name 引数が削除され、引数を取らない Open() メソッドとして修正されました。これにより、インターフェースの定義がGo言語の正しい文法に準拠し、かつ具体的な実装のシグネチャと一致するようになりました。

この変更により、Go言語のインターフェースの概念を説明するコード例が正確になり、読者がインターフェースの正しい使い方を理解できるようになりました。特に、インターフェースのメソッドシグネチャは、実装する型のメソッドシグネチャと完全に一致する必要があるという重要なルールが、この修正によって明確に示されています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント: https://go.dev/doc/
  • Go言語のFAQ: https://go.dev/doc/faq
  • Go言語の仕様書: https://go.dev/ref/spec
  • Go言語のコミット履歴: https://github.com/golang/go/commits/master
  • Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージに記載されている https://golang.org/cl/9564044 はGerritの変更リストへのリンクです)
  • os パッケージのドキュメント: https://pkg.go.dev/os
  • io パッケージのドキュメント: https://pkg.go.dev/io