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

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

このコミットは、Go言語の実験的なexp/locale/collateパッケージ内のcollate.goファイルに対する変更です。このファイルは、ロケール(地域や言語)に応じた文字列の照合(比較・ソート)機能を提供することを目的としていました。具体的には、Locales()関数が返すスライスの利用に関するコメントの明確化が行われています。

コミット

commit 34f2050626bd26a63fc7333555fbbad280abc214
Author: Marcel van Lohuizen <mpvl@golang.org>
Date:   Wed Oct 24 11:40:32 2012 +0200

    exp/locale/collate: clarification in comments on use of returned value.
    
    R=r
    CC=golang-dev
    https://golang.org/cl/6752043

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

https://github.com/golang/go/commit/34f2050626bd26a63fc7333555fbbad280abc214

元コミット内容

exp/locale/collate: clarification in comments on use of returned value.

R=r
CC=golang-dev
https://golang.org/cl/6752043

変更の背景

この変更の背景には、Go言語におけるスライスの特性と、APIの安定性および予期せぬ副作用の防止という設計原則があります。

Go言語のスライスは、基底となる配列への参照を持つデータ構造です。関数がスライスを返す場合、そのスライスは内部のデータ構造(多くの場合、パッケージ内で管理されているプライベートな配列)を指している可能性があります。もし、返されたスライスを呼び出し元が自由に書き換えることができてしまうと、以下のような問題が発生する可能性があります。

  1. 内部状態の破壊: パッケージが管理している内部データが、外部からの意図しない変更によって破壊され、パッケージの機能が正しく動作しなくなる。
  2. 競合状態: 複数のゴルーチンが同じスライスを操作しようとした場合、データ競合が発生し、予測不能な結果やクラッシュを引き起こす可能性がある。
  3. APIの不安定性: パッケージの内部実装が変更された際に、外部のコードがその変更に依存していると、互換性が失われる可能性がある。

Locales()関数は、利用可能なロケール名のリストをスライスとして返します。このリストはパッケージの内部で管理されているため、返されたスライスが変更されると、パッケージ全体の動作に影響を与える可能性があります。このコミットは、このような潜在的な問題を未然に防ぎ、APIの利用者に安全な使い方を明示するために行われました。

前提知識の解説

Go言語におけるスライス

Go言語のスライスは、配列をラップした軽量なデータ構造です。スライスは以下の3つの要素で構成されます。

  • ポインタ: スライスが参照する基底配列の先頭要素へのポインタ。
  • 長さ (length): スライスに含まれる要素の数。
  • 容量 (capacity): スライスが参照する基底配列の、スライスの先頭要素から末尾までの要素の総数。

スライスは参照型のように振る舞いますが、厳密には参照型ではありません。関数にスライスを渡す場合、スライスのヘッダ(ポインタ、長さ、容量)が値渡しされます。しかし、そのヘッダが指す基底配列は共有されるため、関数内でスライスの要素を変更すると、呼び出し元のスライスにもその変更が反映されます。

例えば、func foo(s []string)という関数にスライスmySliceを渡した場合、foo関数内でs[0] = "new value"とすると、mySlice[0]も変更されます。しかし、s = append(s, "another")のようにスライスの長さを変更する操作を行うと、新しい基底配列が割り当てられる可能性があり、その場合、sは新しい基底配列を指すようになりますが、mySliceは元の基底配列を指したままになります。

exp/locale/collateパッケージ

exp/locale/collateは、Go言語の国際化(i18n)および地域化(l10n)を扱うための実験的なパッケージの一部でした。このパッケージの主な目的は、異なる言語や地域(ロケール)の規則に従って文字列を比較し、ソートすることです。例えば、ドイツ語では「ä」は「a」とは異なる文字として扱われたり、特定の文字の並び順が英語とは異なったりします。collateパッケージは、このようなロケール固有の照合ルールを適用するための機能を提供します。

現在、この機能はgolang.org/x/text/collateパッケージとして安定版が提供されています。

Locales()関数の役割

Locales()関数は、collateパッケージがサポートしている、親ロケールとは異なる照合ルールを持つロケール(例: "de" (ドイツ語), "fr" (フランス語) など)のリストを文字列のスライスとして返します。このリストは、ユーザーが利用可能な照合ルールを把握し、適切なCollator(照合器)を作成するために使用されます。

技術的詳細

このコミットは、Locales()関数が返す[]stringスライスが、パッケージ内部で管理されているavailableLocalesというプライベートなスライスを直接参照していることを明確にするためのものです。

Go言語では、スライスは基底配列へのビューを提供します。Locales()関数がreturn availableLocalesとすることで、availableLocalesの基底配列を共有する新しいスライスヘッダが呼び出し元に返されます。

もし呼び出し元が返されたスライスに対してs[0] = "modified"のような要素の変更操作を行った場合、それはavailableLocalesの基底配列の要素を直接変更することになります。これは、パッケージの内部状態を外部から変更することを意味し、以下のような深刻な問題を引き起こす可能性があります。

  • データの一貫性の喪失: availableLocalesはパッケージの内部ロジックで利用されるため、外部からの変更によって予期せぬ動作やバグが発生する可能性があります。
  • 並行処理の問題: 複数のゴルーチンが同時にLocales()を呼び出し、返されたスライスを変更しようとすると、データ競合が発生し、プログラムがクラッシュしたり、不正な状態になったりする可能性があります。
  • APIの契約違反: パッケージの設計者が意図しない方法で内部データが変更されることは、APIの契約(この関数はロケールリストを提供するだけで、そのリストを変更することは許可しない)に違反します。

このため、Go言語の標準ライブラリや高品質なサードパーティライブラリでは、関数が内部のスライスを返す場合、そのスライスが変更されるべきではないことをコメントで明示することが一般的なプラクティスです。これにより、APIの利用者は、返されたスライスを読み取り専用として扱い、必要であればコピーを作成してから変更するなどの適切な対応を取ることができます。

このコミットで追加されたコメント// The returned value should not be modified.は、まさにこの意図を明確に伝えるためのものです。これにより、開発者はこのスライスを安全に利用する方法を理解し、潜在的なバグや競合状態を回避できます。

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

--- a/src/pkg/exp/locale/collate/collate.go
+++ b/src/pkg/exp/locale/collate/collate.go
@@ -86,6 +86,7 @@ type Collator struct {
 }
 
 // Locales returns the list of locales for which collating differs from its parent locale.
+// The returned value should not be modified.
 func Locales() []string {
 	return availableLocales
 }

コアとなるコードの解説

変更はsrc/pkg/exp/locale/collate/collate.goファイルのLocales()関数のコメントに1行追加されただけです。

追加された行: // The returned value should not be modified.

このコメントは、Locales()関数が返す[]string型のスライスが、呼び出し元によって変更されるべきではないことを明示的に示しています。これは、前述の通り、Go言語のスライスの特性上、返されたスライスがパッケージ内部のプライベートなデータ(この場合はavailableLocales)を直接参照しているため、そのスライスを変更するとパッケージの内部状態が破壊される可能性があるためです。

このコメントは、APIの利用者が安全にコードを書くための重要なガイドラインとなります。このコメントを読むことで、開発者は返されたスライスを読み取り専用として扱い、もし変更が必要な場合は、まずスライスのコピーを作成してから操作を行うべきだと理解できます。

例:

// 悪い例: 返されたスライスを直接変更しようとする
locales := collate.Locales()
if len(locales) > 0 {
    locales[0] = "invalid" // これはパッケージの内部状態を破壊する可能性がある
}

// 良い例: コピーを作成してから変更する
locales := collate.Locales()
safeLocales := make([]string, len(locales))
copy(safeLocales, locales)
if len(safeLocales) > 0 {
    safeLocales[0] = "modified" // これなら安全
}

このように、たった1行のコメント追加ですが、Go言語の特性を理解した上でのAPI設計において非常に重要な意味を持ちます。

関連リンク

  • golang.org/x/text/collateパッケージ: 現在のGo言語におけるロケール対応の文字列照合機能を提供するパッケージです。このコミットで変更されたexp/locale/collateは実験的なものであり、現在はgolang.org/x/text/collateが推奨されます。

参考にした情報源リンク

  • GitHubコミットページ: https://github.com/golang/go/commit/34f2050626bd26a63fc7333555fbbad280abc214
  • Go言語におけるスライスの挙動に関する一般的なドキュメントやチュートリアル (例: Go言語の公式ドキュメント、Effective Goなど)
  • golang.org/x/text/collateに関するWeb検索結果 (Web search results for "golang exp/locale/collate"から得られた情報)