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

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

このコミットは、Go言語のツールチェインの一部である cmd/api コマンドの動作を変更し、APIの追加を許可するように修正したものです。これにより、既存のAPIとの互換性を維持しつつ、新しいAPI要素が追加された場合に cmd/api がエラーを報告しないようになります。

コミット

commit edf8b03fef1ab4bf8de7d1f01f3110996a6c9f9c
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Tue Apr 10 22:12:09 2012 +0800

    cmd/api: allow addition of API
    
    R=bradfitz, rsc
    CC=golang-dev
    https://golang.org/cl/5991081

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

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

元コミット内容

    cmd/api: allow addition of API
    
    R=bradfitz, rsc
    CC=golang-dev
    https://golang.org/cl/5991081

変更の背景

Go言語は、その安定したAPI互換性ポリシーで知られています。これは、Go 1のリリース以降、既存のコードが新しいバージョンのGoでも動作し続けることを保証するための重要な原則です。cmd/api ツールは、このAPI互換性を強制するための主要なツールの一つとして機能します。

元々、cmd/api は、Goの標準ライブラリのAPIが以前のバージョンと比較して変更されていないことを検証するために使用されていました。これには、APIの削除、変更、そして追加も含まれていました。つまり、新しい関数、メソッド、型などが追加された場合でも、cmd/api はそれを「変更」とみなし、互換性のない変更としてフラグを立てていました。

しかし、Go言語の進化の過程で、APIの追加は後方互換性を壊すものではなく、むしろ言語の機能拡張や改善のために必要不可欠であることが認識されました。APIの追加は、既存のコードの動作に影響を与えないため、Goの互換性ポリシーに反しません。このコミットは、cmd/api の動作をこの新しい理解に合わせ、APIの追加を互換性のある変更として扱うように修正することを目的としています。これにより、開発者は新しいAPIを安心して追加できるようになり、cmd/api は本当に互換性を壊す変更(APIの削除やシグネチャの変更など)に焦点を当てることができるようになります。

前提知識の解説

cmd/api とは

cmd/api は、Go言語の標準ライブラリのAPI(Application Programming Interface)の互換性をチェックするためのコマンドラインツールです。Go 1の互換性保証の重要な部分を担っており、Goの新しいバージョンがリリースされる際に、以前のバージョンとのAPI互換性が維持されていることを検証するために使用されます。

このツールは、Goのソースコードを解析し、公開されている(エクスポートされた)型、関数、メソッド、定数などのAPI要素を抽出します。そして、現在のAPI定義と、以前のバージョンのAPI定義(通常はGo 1のAPI)を比較します。比較の結果、以下のような変更を検出します。

  • APIの削除: 既存のAPI要素が削除された場合。これは互換性を壊す変更です。
  • APIの変更: 既存のAPI要素のシグネチャ(引数、戻り値、型など)が変更された場合。これも互換性を壊す変更です。
  • APIの追加: 新しいAPI要素が追加された場合。このコミット以前は互換性を壊す変更と見なされていましたが、このコミット以降は許可されるようになりました。

cmd/api は、これらの変更を報告することで、Goのコア開発者がAPI互換性ポリシーに違反する変更を誤って導入するのを防ぐのに役立ちます。

GoのAPI互換性ポリシー

Go言語は、Go 1のリリース以降、「Go 1の互換性保証」という強力なポリシーを掲げています。これは、Go 1で書かれたプログラムは、Goの将来のバージョンでもコンパイルされ、動作し続けることを保証するというものです。このポリシーは、Goが安定したプラットフォームとして広く採用される上で非常に重要な要素となっています。

API互換性ポリシーの主な原則は以下の通りです。

  • 既存のAPIの削除や変更は原則禁止: 一度公開されたAPIは、特別な理由がない限り削除されたり、そのシグネチャが変更されたりすることはありません。これにより、既存のコードが壊れるのを防ぎます。
  • APIの追加は許可: 新しい関数、型、メソッドなどを追加することは、既存のコードの動作に影響を与えないため、互換性のある変更と見なされます。このコミットは、cmd/api がこの原則に沿って動作するように調整するものです。
  • 動作の変更は慎重に: APIのシグネチャは変わらなくても、その動作が変更される場合は、既存のコードに影響を与える可能性があるため、非常に慎重に扱われます。

このポリシーにより、Go開発者は安心してGoの新しいバージョンにアップグレードでき、依存関係の管理やコードのメンテナンスの負担が軽減されます。

技術的詳細

このコミットは、src/cmd/api/goapi.go ファイル内のAPI比較ロジックを修正しています。goapi.go は、GoのAPI定義を読み込み、比較を行う主要なファイルです。

cmd/api は、2つのAPIリスト(通常は古いAPIと新しいAPI)を比較し、差分を検出します。この比較は、API要素をソートされたリストとして扱い、両方のリストを同時に走査することで行われます。

変更が加えられたのは、main 関数内の switch ステートメントの一部です。この switch ステートメントは、古いAPIリスト (v1) と新しいAPIリスト (v2) の要素を比較し、それぞれのケースでどのような処理を行うかを決定します。

元のコードでは、新しいAPIリスト (v2) にのみ存在する要素(つまり、APIの追加)が検出された場合、changes = true と設定していました。これは、APIの追加も互換性のない変更として扱っていたことを意味します。

変更後のコードでは、この changes = true の行が削除され、代わりに // we allow API additions now というコメントが追加されています。これにより、APIの追加が検出されても changes フラグが設定されなくなり、cmd/api はAPIの追加を互換性のある変更として認識し、エラーとして報告しなくなります。

take(&v2) は、新しいAPIリストから現在の要素を取り除き、次の要素に進むためのヘルパー関数です。この操作自体は変更されていません。重要なのは、このケースで changes フラグを設定しないというロジックの変更です。

この修正により、cmd/api はGoのAPI互換性ポリシーの進化に適合し、APIの追加が許可されるという現実を反映するようになりました。

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

--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -147,7 +147,7 @@ func main() {
 				changes = true
 			case len(v1) == 0 || v1[0] > v2[0]:
 				fmt.Fprintf(bw, "+%s\n", take(&v2))
-				changes = true
+				// we allow API additions now
 			default:
 				take(&v1)
 				take(&v2)

コアとなるコードの解説

変更されたコードは src/cmd/api/goapi.go ファイルの main 関数内にあります。

			case len(v1) == 0 || v1[0] > v2[0]:
				fmt.Fprintf(bw, "+%s\n", take(&v2))
-				changes = true
+				// we allow API additions now

この case 文は、APIの比較ロジックの一部です。

  • len(v1) == 0: これは、古いAPIリスト (v1) が空になったことを意味します。つまり、新しいAPIリスト (v2) にはまだ要素が残っているが、古いAPIには対応する要素がない状態です。これは新しいAPIの追加を意味します。
  • v1[0] > v2[0]: これは、新しいAPIリストの現在の要素 (v2[0]) が、古いAPIリストの現在の要素 (v1[0]) よりも「小さい」ことを意味します(ソート順に基づきます)。これは、古いAPIには存在しない新しいAPI要素が v2 に追加されたことを示唆します。

これらの条件のいずれかが真である場合、それは新しいAPI要素が追加されたことを示します。

元のコードでは、この case のブロック内で changes = true という行がありました。これは、APIの追加が検出された場合に、changes というブール変数に true を設定していました。この changes 変数は、最終的に cmd/api が互換性のない変更を検出したかどうかを示すために使用されていました。changestrue になると、cmd/api はエラーコードを返したり、変更を報告したりしていました。

このコミットでは、changes = true の行が削除され、代わりに // we allow API additions now というコメントが追加されています。この変更により、APIの追加が検出されても changes 変数は true に設定されなくなります。結果として、cmd/api はAPIの追加を互換性のない変更とは見なさなくなり、エラーとして報告しなくなります。

fmt.Fprintf(bw, "+%s\n", take(&v2)) の行は、追加されたAPI要素を標準出力(または指定された出力ストリーム)に + プレフィックスを付けて表示する役割を担っています。これはデバッグや情報提供のために残されていますが、この変更によってツールの終了ステータスには影響を与えなくなります。

関連リンク

参考にした情報源リンク

  • コミット: edf8b03fef1ab4bf8de7d1f01f3110996a6c9f9c (この解説の対象となるコミット自体)
  • Go言語の公式ドキュメント (特にGo 1の互換性保証に関するセクション)
  • cmd/api ツールのソースコード