math/rand/v2: add Rand.N
要約
概要
math/rand/v2 パッケージには、任意の整数型に対して動作するジェネリック関数 N[Int intType](n Int) Int が既に存在するが、対応するメソッド版 (*Rand).N がない。Go言語にジェネリックメソッドが追加されること(#77273)を受けて、同パッケージの Rand 型にも同等のメソッドを追加するというプロポーザルである。
ステータス変更
likely_accept → accepted
2026年5月6日に aclements(Proposal Review グループ)が "likely accept" と判定し、その後コミュニティから反対意見が出なかったため、2026年5月13日に正式に accepted となった。あわせて実装CL(go.dev/cl/775100)がすでに提出されており、実装段階へ移行している。
技術的背景
現状の問題点
math/rand/v2 パッケージにはジェネリック関数 N が存在し、デフォルトのグローバルソースから任意の整数型の乱数を取得できる。しかし、独自の *Rand インスタンス(特定のシードや独立したランダムソース)を使いたい場合、型ごとの個別メソッド(IntN、Int64N、Uint32N など)を呼び出すしかなく、型に応じてコードを書き分ける必要があった。
Go言語の仕様上、メソッドに独自の型パラメータを持たせることができなかったため(#49085 から長年の要望だったが未実現)、math/rand/v2 設計時点では Rand.N メソッドを追加できなかった。
提案された解決策
#77273(Go言語へのジェネリックメソッド追加)が承認されたことを受け、以下のメソッドを math/rand/v2 の Rand 型に追加する:
// N returns a pseudo-random number in the half-open interval [0,n).
// The type parameter Int can be any integer type.
// It panics if n <= 0.
func (r *Rand) N[Int intType](n Int) Int
これにより、カスタムの *Rand インスタンスを使いつつ、任意の整数型で乱数を生成できる。
これによって何ができるようになるか
カスタムソースを持つ *Rand 型でも、グローバル関数 rand.N と同様のジェネリックな使い方が可能になる。型ごとに異なるメソッドを選ぶ手間がなくなり、コードがシンプルになる。
コード例
// Before: 型ごとに個別メソッドを使う必要があった
r := rand.New(rand.NewPCG(1, 2))
n64 := r.Int64N(100) // int64 専用
n32 := r.Uint32N(100) // uint32 専用
dur := r.Int64N(int64(100 * time.Millisecond)) // Duration には変換が必要
// After: Rand.N メソッドで任意の整数型に対応
r := rand.New(rand.NewPCG(1, 2))
n64 := r.N(int64(100)) // int64
n32 := r.N(uint32(100)) // uint32
dur := r.N(100 * time.Millisecond) // time.Duration もそのまま使用可能
議論のハイライト
- 追加の動機: 既存の
rand.N関数(グローバルソース用)と(*Rand).Nメソッド(カスタムソース用)の非対称性を解消することが主な目的である。 - 言語機能への依存: このプロポーザルはGo言語へのジェネリックメソッド追加(#77273)が前提となっており、「#77273 実装後に追加する」と明示されている。
- 設計上の対称性:
math/rand/v2にはN(グローバル)と(*Rand).IntNなどが共存しているが、本提案で(*Rand).Nを追加することでAPIの一貫性が保たれる。 - 反対意見なし: likely_accept 判定後、コミュニティからの異論がなく、約1週間でスムーズに accepted へ移行した。
- 実装の迅速な開始: likely_accept 判定の翌日(2026年5月7日)にすでに実装CL(go.dev/cl/775100)が提出されており、実装準備は整っていた。