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

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

このコミットは、Go言語のコマンドラインツール cmd/go におけるエラーメッセージの改善に関するものです。具体的には、パッケージのインポートサイクル(循環参照)が検出された際に表示されるエラーメッセージを、より明確で理解しやすいものに変更しています。

コミット

commit 508bfda6d3dd40e2906fb6538b5e4db0ba129645
Author: Russ Cox <rsc@golang.org>
Date:   Sat Sep 1 10:34:58 2012 -0400

    cmd/go: be clear that import loops are bad
    
    There was mail on golang-nuts a few weeks ago
    from someone who understood the message perfectly
    and knew he had a cyclic dependency but assumed
    that Go, like Python or Java, was supposed to handle it.
    
    R=golang-dev, bradfitz, dave
    CC=golang-dev
    https://golang.org/cl/6488069

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

https://github.com/golang/go/commit/508bfda6d3dd40e2906fb6538b5e4db0ba129645

元コミット内容

このコミットは、Go言語の cmd/go ツールがインポートループ(循環参照)を検出した際のエラーメッセージを「import loop」から「import cycle not allowed」に変更するものです。

変更の背景

コミットメッセージに記載されている通り、この変更は golang-nuts メーリングリストでの議論がきっかけとなっています。あるユーザーが、Go言語がPythonやJavaのように循環依存を許容するものと誤解し、Goのインポートループエラーメッセージを理解していながらも、それが許容されるべきだと考えていたことが判明しました。

Go言語の設計思想では、パッケージ間の循環参照は許容されません。これは、コンパイルの複雑さを軽減し、コードの依存関係を明確に保つための重要な原則です。しかし、既存のエラーメッセージ「import loop」だけでは、この「許容されない」というニュアンスが十分に伝わっていませんでした。

この背景から、ユーザーの誤解を解消し、Go言語の設計原則をより明確に伝えるために、エラーメッセージを「import cycle not allowed」(インポートサイクルは許可されていません)という、より直接的で断定的な表現に変更する必要性が生じました。

前提知識の解説

Go言語のパッケージとインポート

Go言語では、コードは「パッケージ」という単位で整理されます。パッケージは関連する機能の集合であり、他のパッケージからその機能を利用するためには import ステートメントを使用します。例えば、import "fmt" は標準ライブラリの fmt パッケージをインポートし、その中の関数(例: fmt.Println)を利用できるようにします。

循環参照(Import Cycle)

循環参照とは、複数のパッケージが互いに直接的または間接的に依存し合っている状態を指します。例えば、package Apackage B をインポートし、同時に package Bpackage A をインポートしている場合、これは直接的な循環参照です。また、package A -> package B -> package C -> package A のように、複数のパッケージを介して最終的に自身に戻ってくる場合も循環参照となります。

Go言語における循環参照の扱い

Go言語は、設計上、パッケージ間の循環参照を厳しく禁止しています。これは以下の理由によります。

  1. コンパイルの複雑性軽減: 循環参照が存在すると、コンパイラはどのパッケージを先にコンパイルすべきかを決定できなくなり、コンパイルプロセスが複雑になります。循環参照を禁止することで、コンパイル順序が一意に定まり、コンパイルが高速かつシンプルになります。
  2. コードの明確性: 循環参照は、コードの依存関係を不明瞭にし、理解やメンテナンスを困難にします。依存関係が一方通行であることで、コードの構造がより明確になり、変更の影響範囲を把握しやすくなります。
  3. デッドロックの回避: 特に並行処理を扱う場合、循環参照はデッドロック(複数の処理がお互いのリソースを待ち続け、進行不能になる状態)を引き起こす可能性があります。パッケージレベルでの循環参照の禁止は、このような問題の発生を未然に防ぐ一助となります。

PythonやJavaなどの一部の言語では、実行時に循環参照を解決するメカニズムを持つものもありますが、Go言語はコンパイル時にこれをエラーとして扱います。

cmd/go ツール

cmd/go は、Go言語のソースコードを管理、ビルド、テスト、実行するための公式コマンドラインツールです。go buildgo rungo testgo get など、Go開発者が日常的に使用する多くのコマンドを提供しています。このツールは、Goのパッケージシステムと密接に連携しており、パッケージの依存関係の解決や、循環参照の検出などもその役割の一部です。

技術的詳細

このコミットの技術的なポイントは、Go言語のビルドシステムがインポートサイクルをどのように検出しているか、そしてその検出結果をユーザーにどのように伝えているか、という点にあります。

Goのビルドプロセスでは、パッケージの依存関係グラフが構築されます。このグラフは有向非巡回グラフ(DAG: Directed Acyclic Graph)である必要があります。もしグラフ中にサイクル(閉路)が検出された場合、それはインポートサイクルとして認識され、ビルドエラーとなります。

src/cmd/go/pkg.go ファイルは、cmd/go ツールのパッケージ管理ロジックの一部を担っています。特に、reusePackage 関数は、パッケージのインポートスタック(どのパッケージがどのパッケージをインポートしているかの履歴)を管理し、循環参照を検出する役割を担っています。

以前のエラーメッセージ「import loop」は、技術的には正しい表現でしたが、そのメッセージだけでは「なぜそれが問題なのか」「Go言語では許容されないのか」という点が不明瞭でした。多くのプログラミング言語では循環参照が許容されるケースがあるため、Goの厳格なポリシーを知らない開発者にとっては混乱を招く可能性がありました。

新しいエラーメッセージ「import cycle not allowed」は、以下の点で優れています。

  • 「cycle」: 「loop」よりも、依存関係の「循環」という概念をより正確に伝えます。
  • 「not allowed」: 「許可されていない」という明確な禁止の意思表示が含まれており、Go言語の設計原則を直接的に伝えます。これにより、ユーザーはGoが他の言語とは異なり、循環参照を許容しないことを明確に理解できます。

この変更は、単なる文字列の変更以上の意味を持ちます。それは、Go言語の設計思想と、それをユーザーに正しく伝えるためのUX(ユーザーエクスペリエンス)の改善を反映しています。エラーメッセージは、開発者が問題を理解し、解決するための重要な手がかりとなるため、その明確性は非常に重要です。

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

変更は src/cmd/go/pkg.go ファイルの1箇所のみです。

--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -250,7 +250,7 @@ func reusePackage(p *Package, stk *importStack) *Package {
 		if p.Error == nil {
 			p.Error = &PackageError{
 				ImportStack: stk.copy(),
-				Err:         "import loop",
+				Err:         "import cycle not allowed",
 			}
 		}
 		p.Incomplete = true

コアとなるコードの解説

この変更は、src/cmd/go/pkg.go ファイル内の reusePackage 関数にあります。

reusePackage 関数は、Goのビルドシステムがパッケージの依存関係を解決する際に呼び出されます。この関数は、既に処理中のパッケージが再度インポートされようとした場合(すなわち、循環参照が検出された場合)に、エラーを生成します。

変更前のコードでは、PackageError 構造体の Err フィールドに "import loop" という文字列が設定されていました。 変更後のコードでは、この文字列が "import cycle not allowed" に変更されています。

PackageError は、Goのパッケージ処理中に発生したエラーをカプセル化するための構造体です。ImportStack フィールドは、循環参照に至ったインポートパスの履歴を保持し、Err フィールドはエラーの具体的なメッセージを保持します。

このシンプルな文字列の変更により、Goのビルドツールがインポートサイクルを検出した際に、より明確で誤解の余地のないエラーメッセージをユーザーに提供できるようになりました。これにより、Go言語のパッケージ依存関係のルールに関する開発者の理解が深まり、不必要な混乱が避けられることが期待されます。

関連リンク

  • Go言語のパッケージに関する公式ドキュメント: https://go.dev/doc/code
  • Go言語のパッケージと依存関係に関する議論(Go Modulesなど): https://go.dev/blog/using-go-modules (Go Modulesはコミット当時のGoのパッケージ管理とは異なりますが、依存関係の概念は共通です)

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード(src/cmd/go/pkg.go
  • golang-nuts メーリングリストのアーカイブ(具体的なスレッドは特定できませんでしたが、コミットメッセージから存在が示唆されています)
  • Go言語における循環参照に関する一般的な技術記事やブログポスト