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

Go Proposal Weekly Digest

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

#79042accepted

go/constant: add StringLen function

ステータス変更: likely_accept accepted

要約

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

概要

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

ステータス変更

likely_acceptaccepted
2026年5月6日に @aclements が「likely accept」と判定し、その後1週間の最終コメント期間を経て異論がなかったため、2026年5月13日に正式に accepted となりました。

技術的背景

現状の問題点

go/constant パッケージは、Goプログラムを静的解析する際に使用されるパッケージで、定数値を抽象的に表現します。
文字列定数の長さを取得しようとすると、現在は StringVal を呼び出す必要がありました。しかし、文字列定数が複数のサブ文字列の連結で作られている場合(例: a + a + a + ...)、StringVal は内部的に木構造で保持されているすべての部分文字列を一つの文字列に実体化(マテリアライズ)します。これにより、巨大なメモリアロケーションが発生します。
関連issue #78346 では、このような巨大な文字列定数の連鎖倍増(constant doubling)が []rune 変換と組み合わさることで、OOM(Out of Memory)を引き起こすバグが報告されています。

// 問題の例:このような定数は go/constant 内部でツリー構造として保持される
const (
    a = "x"
    b = a + a  // 内部: ツリー(葉は "x")
    c = b + b  // 内部: ツリー(倍々に拡大)
    // ... 繰り返すと 2^N 文字の巨大な論理的文字列になる
    Z = Y + Y  // 2^52文字 = 約4PB相当(実体化すると OOM)
)

StringVal(Z) を呼び出すと、この巨大な文字列を実際にメモリ上に構築しようとしてしまいます。

提案された解決策

// 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ビットプラットフォームで動作する静的解析ツールが64ビットターゲット向けプログラムを解析する際に、math.MaxInt32 を超える文字列長が論理的に存在しうるためです(ianlancetaylorgriesemer が議論で確認)。

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

コード例

// Before: 従来の書き方(巨大な文字列の場合、OOM の恐れあり)
import "go/constant"
func getStringLength(v constant.Value) int {
    s := constant.StringVal(v) // 巨大な文字列を実体化してしまう
    return len(s)
}
// After: 新APIを使った書き方(アロケーションなし)
import "go/constant"
func getStringLength(v constant.Value) int64 {
    return constant.StringLen(v) // 文字列を実体化せずに長さだけ取得
}

実践的なユースケース:

  1. 静的解析ツール: go/types が文字列定数の長さを検証する際(例: [N]byte 型へのキャストの妥当性チェック)に、安全かつ効率的に長さを取得できる
  2. コンパイラの定数評価: 文字列定数が規定サイズを超えているかをチェックし、OOM を防ぐためのガード条件として使用できる(#78346 の修正に直接関係)
  3. コード検査ツール: 文字列定数のサイズ制限を実装するリンターや検査ツールが、実際の文字列を生成せずに安全にサイズを確認できる

議論のハイライト

  • int64 vs int の返り値型: @mateusz834int で十分ではないかと疑問を呈したが、@griesemer@ianlancetaylor が「32ビットホストが64ビットターゲット向けプログラムを解析する際、論理的に math.MaxInt32 を超える文字列長がありえる」と説明し、int64 が採用された
  • メソッド vs 関数: @mateusz834Value インターフェースにメソッドとして追加できると指摘したが、@griesemer は「go/constant パッケージの他の機能はすべて関数ベースで設計されており、一貫性のため関数として追加するのが適切」と判断した
  • 巨大な文字列定数の実在性: 議論の中で、定数の倍増による理論上の巨大文字列(実際には実体化されない)が go/constant パッケージ内で合法的に存在することが確認された
  • #78346 との関連: この提案は、コンパイラがメモリ不足になるバグ(#78346)への対応策として位置づけられており、文字列を実体化せずサイズチェックを行うためのAPI基盤となる

関連リンク