spec: remove string(int)
要約
概要
string(int) という、整数を文字列に変換する言語仕様上の型変換を、将来のGoバージョンで廃止することを検討するproposalです。今回、長期間「保留」状態だったこの提案が、正式に「active」(毎週のproposalレビュー会議で審議される)ステータスへ移行しました。
ステータス変更
(未設定/保留) → active
2026年7月時点では、go vetによるチェックが長年運用されており依存コードは少ないと見られる一方、削除によるメリットも乏しいため「likely decline(却下見込み)」とされていました。しかし@aclements氏が「言語バージョンに紐付ければ既存コードを壊さずに済み、go vetより確実なコンパイルエラーとして検出できる」という利点を指摘したことで議論が再燃。@neild氏が「go1.28以降の言語バージョンでは、byte・rune以外の整数型からstringへの変換を無効にする」という具体的な提案内容を整理し、8月12日に一般的なproposalレビューに移管された後、8月19日の会議を経て「active」列に追加されました。
技術的背景
現状の問題点
Goの言語仕様では、整数値をstringに変換すると、その整数値をUnicodeコードポイントとして扱いUTF-8エンコードされた文字列に変換します。これは初期のGoでフォーマット出力をブートストラップするために導入された古い機能で、以下の問題があります。
i := 3
s := string(i) // "\x03" になる("3" ではない)
string(0xD800)のようにUTF-8サロゲート範囲の値を渡すと、期待通りのエンコード結果にならず、代わりに置換文字�が返されるstring(0xD800)と"\uD800"が異なる挙動(前者はU+FFFDのUTF-8表現、後者は静的に拒否される)になる一貫性のなさ- Python等の他言語の
str(42)のような「数値の文字列表現を返す」という誤解を招きやすい(実際に複数のユーザーが誤用してバグを報告) - シフト演算を伴う場合の挙動の複雑さ(関連issue #26096)
提案された解決策
言語バージョンがgo1.28以上の場合、byte(uint8)とrune(int32)以外の整数型からstringへの変換をコンパイルエラーにする、という段階的な提案です。go.modの言語バージョン指定に基づくため、既存のモジュールが古い言語バージョンを指定していれば挙動は変わらず、後方互換性が保たれます。byteとruneを除外から外さない理由は、既存のgo vetチェックがこの2つを許容していることと、string(os.PathSeparator)のような実用的なコードが多数存在するためです。
これによって何ができるようになるか
この変更が実現すると、string(int)の誤用によるバグをコンパイル時に検出できるようになり、go vetだけに頼っていた不完全な静的解析から、より確実な言語レベルの保証へと移行できます。
想定されるユースケースは以下の通りです。
- 型の異なる整数(
int,int64など)を誤ってstringに変換してしまうバグを、CI/CDやビルド時に確実に検出したい開発チーム - ライブラリ設計者が、意図しない変換を防ぐためにAPIの安全性を高めたい場合
- Go言語の教育において、
string(int)という直感に反する挙動を仕様から排除し、学習者の混乱を減らしたい場合
コード例
// Before: 意図しない変換が黙って通ってしまう(数値の文字列化と誤解しやすい)
count := 42
label := string(count) // "*" (U+002A) になり、コンパイルエラーにならない
// After (go1.28以降の言語バージョンを指定した場合):
// string(count) はコンパイルエラーになる
label := strconv.Itoa(count) // "42" が欲しい場合はこちらを使う
// rune/byte への変換は引き続き許可される
r := rune(65)
s := string(r) // "A" (これは今後も有効)
議論のハイライト
- byteとruneは特例として存続: @ianlancetaylor氏が指摘した通り、
string([]byte)やstring([]rune)と対称性を保つため、単一のbyte・runeからstringへの変換は引き続き許可される方針で合意が形成されています。 - 定数式への影響: @griesemer氏は、
const s = string(c)のような定数コンテキストでの利用が今後不可能になる可能性を指摘し、varへの置き換えや手動実装が必要になるケースがあると説明しています。 - 言語バージョン連動という妥協案: 当初は完全撤廃も検討されましたが、Go 1互換性の問題(既存コードを壊すリスク)から、
go.modの言語バージョンに応じて挙動を切り替える方式が有力となりました。これにより既存コードへの影響を最小化しつつ、新規コードでは安全性を高められます。 - 「likely decline」から「active」への転換: 一度は削除の実益が乏しいとして却下寄りの判断がされましたが、コンパイルエラーとして検出できることの価値(
go vetは必ずしも全員が実行するとは限らない)が再評価され、議論が継続することになりました。 - 代替案の検討と却下:
charという新しい組み込み関数の追加が提案されましたが、@ianlancetaylor氏により「現行のstring変換と本質的な違いがない」として退けられました。