メインコンテンツへスキップ

Go Proposal Weekly Digest

Go言語のproposal更新を毎週お届け

#79042likely_accept

go/constant: add StringLen function

新規提案

要約

AIによる要約であり、誤りを含む場合があります。

概要

go/constant パッケージに StringLen 関数を追加する提案です。この関数は、文字列定数の長さを、実際の文字列値を構築(アロケーション)することなく取得できるようにします。

ステータス変更

(なし)likely_accept
2026年5月6日のProposal Reviewミーティングにて、@aclementsがproposalレビューグループを代表して likely accept と判断しました。提案されたAPIがシンプルで明確であること、実装CL(go.dev/cl/772320)がすでに提出済みであること、議論の中で返り値の型として int64 を採用する理由も明確に説明されたことが、スムーズな承認につながったと考えられます。

技術的背景

現状の問題点

go/constant パッケージは、Goプログラムの静的解析において定数値を表現するためのパッケージです。文字列定数の長さを知りたい場合、現在は StringVal 関数を使って実際の文字列値を取得してから len() を呼ぶ必要があります。
しかし StringVal には大きな問題があります。go/constant パッケージ内部では、文字列の結合(+ 演算子)によって生成された定数は、即座に単一の文字列に展開されるのではなく、「部分文字列のツリー(連結木)」として遅延評価的に保持されます。StringVal を呼ぶと、この連結木から実際の文字列を構築するため、大規模なメモリアロケーションが発生します。
関連issue #78346 では、文字列定数を繰り返し倍増させるパターン(b = a + a, c = b + b, ...)でコンパイラがOOM(メモリ枯渇)する問題が報告されています。例えば以下のようなコードが問題を引き起こします。

const (
    a = "x"
    b = a + a  // 長さ2
    c = b + b  // 長さ4
    // ... 繰り返し倍増
    Z = Y + Y  // 理論上は2^52バイト以上
)
var _ = []rune(Z[:])  // StringVal相当の処理でOOM

提案された解決策

以下の関数を go/constant パッケージに追加します。

// StringLen returns the length of x if x is a [String].
// If x is [Unknown], the result is 0.
// In all other cases, the function panics.
func StringLen(x Value) int64

内部の連結木を実際の文字列として展開することなく、ツリーのノード数や部分文字列の長さを再帰的に加算するだけで長さを計算できるため、アロケーションを回避できます。返り値が int ではなく int64 である理由は、連結木として保持される文字列は32ビットプラットフォームの math.MaxInt を超えた長さを持つ可能性があるためです(実際に文字列をメモリ上に展開しない限り、論理的に巨大な文字列定数が定義できます)。

これによって何ができるようになるか

go/constant パッケージを使う静的解析ツールやコンパイラが、文字列定数の長さを効率的に検査できるようになります。

コード例

// Before: StringVal を使った従来の方法(大規模な文字列でOOMの危険)
import "go/constant"
func checkStringLen(val constant.Value) {
    str := constant.StringVal(val) // 連結木を展開→大量アロケーション
    if len(str) > maxLen {
        // エラー処理
    }
}
// After: StringLen を使った安全な方法
import "go/constant"
func checkStringLen(val constant.Value) {
    length := constant.StringLen(val) // アロケーションなしで長さを取得
    if length > maxLen {
        // エラー処理
    }
}

議論のハイライト

  • 返り値の型をなぜ int64 にするか: 最初 int で良いのではという意見が出たが、go/constant は32ビットホスト上で64ビットターゲット向けプログラムを解析する用途があり、連結木として保持される文字列長は理論上 math.MaxInt32 を超え得るため int64 が適切と結論付けられた。
  • なぜメソッドではなく関数か: Value インターフェースはエクスポートされていないメソッド implementsValue() を持つため技術的にはメソッドも可能だが、go/constant パッケージの他の機能が一貫して関数スタイルを採用しているため、関数として提供することが適切と @griesemer が説明した。
  • 関連するOOM問題 (#78346): 文字列定数の倍増パターンでコンパイラがOOMする問題への対処として go/types 側でサイズチェックを追加するCLが提出されたが、一旦無効化された経緯がある。本提案の StringLen はそのチェックを安全に実装するための基盤となる。
  • 既存APIとの差異: 既存の StringVal は文字列を実体化するため、長さチェックだけの用途には過剰なコストがかかる。StringLen はその不足を補う最小限のAPIとして設計されている。
  • 実装CLはすでに提出済み (go.dev/cl/772320): proposalと並行して実装が進められており、承認後すぐに取り込める状態にある。

関連リンク