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

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

このコミットは、Go言語の公式仕様書である doc/go_spec.html ファイルに対する変更です。このファイルは、Go言語の構文、セマンティクス、および型システムに関する詳細な定義を提供し、Go言語の挙動を理解するための最も権威ある情報源の一つです。今回の変更は、インターフェースの埋め込みに関する特定の制約、特に再帰的な埋め込みの禁止を明確にするものです。

コミット

commit 388816ae078ed93ec409dca8024638eb7ca774d7
Author: Russ Cox <rsc@golang.org>
Date:   Wed Feb 8 14:35:00 2012 -0500

    spec: disallow recursive embedded interfaces
    
    Fixes #1814.
    
    R=golang-dev, gri
    CC=golang-dev
    https://golang.org/cl/5647054

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

https://github.com/golang/go/commit/388816ae078ed93ec409dca8024638eb7ca774d7

元コミット内容

spec: disallow recursive embedded interfaces

Fixes #1814.

R=golang-dev, gri
CC=golang-dev
https://golang.org/cl/5647054

変更の背景

このコミットの背景には、Go言語のインターフェース型システムにおける潜在的な曖昧さや、無限再帰による型定義の問題を防ぐという目的があります。Go言語のインターフェースは、メソッドの集合を定義する強力な機能であり、他のインターフェースを「埋め込む」ことで、そのメソッド集合を継承する形で拡張できます。しかし、インターフェースが直接的または間接的に自分自身を埋め込むような再帰的な定義を許容すると、型システムの健全性が損なわれ、コンパイラが型を解決できなくなったり、無限ループに陥ったりする可能性があります。

このコミットは、Go言語のIssue #1814(このコミットが修正したとされるIssueであり、一般的なWeb検索で出てくる同一番号の他のIssueとは異なります)に対応するもので、このような再帰的なインターフェースの埋め込みを言語仕様として明示的に禁止することで、Go言語の型システムの堅牢性と予測可能性を保証することを目的としています。これにより、開発者が意図しない再帰的な型定義によってコンパイルエラーや予期せぬ挙動に遭遇するのを防ぎます。

前提知識の解説

Go言語のインターフェース

Go言語におけるインターフェースは、メソッドのシグネチャ(名前、引数、戻り値)の集合を定義する型です。インターフェースは、そのインターフェースで定義されたすべてのメソッドを実装する任意の型によって「実装」されます。Goのインターフェースは、JavaやC++のような明示的なimplementsキーワードを必要とせず、型がインターフェースのメソッドセットを満たしていれば、自動的にそのインターフェースを実装しているとみなされます(ダックタイピング)。これにより、柔軟なポリモーフィズムと疎結合な設計が可能になります。

例:

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

type Writer interface {
    Write(p []byte) (n int, err error)
}

インターフェースの埋め込み (Interface Embedding)

Go言語では、構造体と同様に、インターフェースも他のインターフェースを「埋め込む」ことができます。インターフェースの埋め込みは、あるインターフェースが別のインターフェースのメソッドセットをすべて含むことを意味します。これにより、既存のインターフェースを再利用し、より大きなインターフェースを構成することが容易になります。

例:

type ReadWriter interface {
    Reader // Readerインターフェースのメソッドを埋め込む
    Writer // Writerインターフェースのメソッドを埋め込む
}

このReadWriterインターフェースは、ReadメソッドとWriteメソッドの両方を持つことになります。

再帰 (Recursion)

プログラミングにおける再帰とは、関数やデータ構造が自分自身を定義の一部として参照する概念です。関数が自分自身を呼び出すことを「再帰呼び出し」といい、データ構造が自分自身と同じ型の要素を含むことを「再帰的なデータ構造」といいます。再帰は、ツリー構造の走査や、特定のアルゴリズムの実装に非常に強力なツールですが、適切に終了条件が設定されていないと、無限ループやスタックオーバーフローを引き起こす可能性があります。

技術的詳細

Go言語の型システムは、型の定義が明確で有限であることを前提としています。インターフェースの再帰的な埋め込みを許容すると、この前提が崩れる可能性があります。

例えば、以下のような再帰的なインターフェース定義を考えてみましょう。

type Bad interface {
    Bad // Badインターフェースが自分自身を埋め込む
}

この定義では、BadインターフェースはBadインターフェースのすべてのメソッドを含むことになります。しかし、Badインターフェースのメソッドセットを決定するためには、まずBadインターフェース自体を解決する必要があります。これは循環参照となり、コンパイラがBadインターフェースの完全なメソッドセットを決定することが不可能になります。結果として、コンパイル時に無限ループに陥るか、未定義の動作を引き起こす可能性があります。

同様に、間接的な再帰も問題となります。

type Bad1 interface {
    Bad2
}
type Bad2 interface {
    Bad1
}

この場合、Bad1Bad2を埋め込み、Bad2Bad1を埋め込みます。これもまた循環参照となり、どちらのインターフェースのメソッドセットも確定できません。

Go言語のコンパイラは、型チェックの際にインターフェースのメソッドセットを解決する必要があります。再帰的な埋め込みが存在すると、この解決プロセスが終了しなくなり、コンパイラが停止したり、誤った型情報を生成したりする原因となります。したがって、言語仕様レベルでこのような再帰的な埋め込みを禁止することは、Go言語の型システムの健全性を保ち、コンパイラの設計を簡素化し、予測可能なコンパイル結果を保証するために不可欠です。

この変更は、Go言語の設計哲学である「シンプルさ」と「明確さ」にも合致しています。複雑で曖昧な型定義を排除することで、コードの可読性と保守性を向上させ、開発者がより堅牢なアプリケーションを構築できるようにします。

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

diff --git a/doc/go_spec.html b/doc/go_spec.html
index ff7ce325ca..8b2d515df0 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1120,9 +1120,10 @@ they implement the <code>Lock</code> interface as well
 as the <code>File</code> interface.
 </p>
 <p>
-An interface may contain an interface type name <code>T</code>
+An interface may use an interface type name <code>T</code>
 in place of a method specification.
-The effect is equivalent to enumerating the methods of <code>T</code> explicitly
+The effect, called embedding an interface,
+is equivalent to enumerating the methods of <code>T</code> explicitly
 in the interface.
 </p>
 
@@ -1139,6 +1140,26 @@ type File interface {
 }\n </pre>\n \n+<p>\n+An interface definition for type <code>T</code> may not embed itself,\n+nor any interface type that embeds <code>T</code> directly or indirectly.\n+</p>\n+\n+<pre>\n+// illegal: Bad cannot embed itself\n+type Bad interface {\n+\tBad\n+}\n+\n+// illegal: Bad1 cannot embed itself using Bad2\n+type Bad1 interface {\n+\tBad2\n+}\n+type Bad2 interface {\n+\tBad1\n+}\n+</pre>\n+\n <h3 id=\"Map_types\">Map types</h3>\n \n <p>\n```

## コアとなるコードの解説

このコミットは、`doc/go_spec.html` ファイルのインターフェースに関するセクションに、再帰的な埋め込みを禁止する新しいルールと具体例を追加しています。

1.  **既存の説明の修正**:
    変更前の記述:
    `An interface may contain an interface type name <code>T</code> in place of a method specification. The effect is equivalent to enumerating the methods of <code>T</code> explicitly in the interface.`
    変更後の記述:
    `An interface may use an interface type name <code>T</code> in place of a method specification. The effect, called embedding an interface, is equivalent to enumerating the methods of <code>T</code> explicitly in the interface.`
    この変更は、`contain`を`use`に、そして「効果」を「インターフェースの埋め込みと呼ばれる効果」とより明確に表現することで、インターフェースの埋め込みの概念をより正確に定義しています。

2.  **再帰的な埋め込みの禁止に関する新しい段落の追加**:
    ```html
    <p>
    An interface definition for type <code>T</code> may not embed itself,
    nor any interface type that embeds <code>T</code> directly or indirectly.
    </p>
    ```
    この新しい段落は、インターフェース`T`が自分自身を直接的または間接的に埋め込むことを明示的に禁止する、言語仕様の新しい制約を導入しています。これは、Go言語の型システムが健全性を保つために不可欠なルールです。

3.  **禁止された再帰的埋め込みの例の追加**:
    ```html
    <pre>
    // illegal: Bad cannot embed itself
    type Bad interface {
    	Bad
    }

    // illegal: Bad1 cannot embed itself using Bad2
    type Bad1 interface {
    	Bad2
    }
    type Bad2 interface {
    	Bad1
    }
    </pre>
    ```
    このコードブロックは、上記で説明された再帰的なインターフェースの埋め込みの2つの具体的な例を示しています。
    *   `Bad`インターフェースが自分自身を直接埋め込むケース。
    *   `Bad1`と`Bad2`が互いを埋め込むことで間接的に再帰するケース。
    これらの例は、どのようなパターンが「不正 (illegal)」と見なされるかを明確にし、開発者がこのような誤った定義を避けるための具体的な指針を提供します。

これらの変更は、Go言語のインターフェースのセマンティクスをより厳密に定義し、コンパイラが型を正しく解決できるようにするための重要な仕様の明確化です。

## 関連リンク

*   Go言語の変更リスト: [https://golang.org/cl/5647054](https://golang.org/cl/5647054)
    (このコミットが修正したとされるIssue #1814は、一般的なWeb検索で出てくる同一番号の他のIssueとは異なります。上記の変更リストがこのコミットに直接関連する情報源です。)

## 参考にした情報源リンク

*   Go言語におけるインターフェースの埋め込みと再帰に関する解説: [https://boldlygo.tech/posts/2023/07/24/go-interface-recursion/](https://boldlygo.tech/posts/2023/07/24/go-interface-recursion/)
*   Go言語のインターフェース埋め込みの基本: [https://www.geeksforgeeks.org/interface-embedding-in-go-language/](https://www.geeksforgeeks.org/interface-embedding-in-go-language/)
*   Go 1.18以降のインターフェースの変更点に関する情報(参考): [https://go.dev/blog/go1.18](https://go.dev/blog/go1.18)
*   Go言語におけるインターフェースの埋め込みと構造体への埋め込みの違いに関する議論: [https://stackoverflow.com/questions/20066900/what-is-the-difference-between-embedding-an-interface-in-a-struct-and-embedding](https://stackoverflow.com/questions/20066900/what-is-the-difference-between-embedding-an-interface-in-a-struct-and-embedding)