Go 1.20.0 にて math/rand のグローバルな乱数生成器のタネが固定値では無くなった

Go 1.20.0 が先日リリースされた。

気になる機能はいくつかあるが、職場にて math/rand パッケージが話題に上がっていたのでチェックしてみた。

ちなみに arena パッケージの実験的サポート、errors.Join 、context.Context の Cause あたりが気になる機能である。

Homebrew でのサポート時間差問題

例の如く Homebrew で最新バージョンがインスコできるまで若干ラグが生じる。(1wくらい?)

プログラミング言語処理系とかCLIツールは Homebrew 管理で統一したいので、我慢するかpkgでインスコするかどちらかを選ぶ必要があるか?と思ったが、Dockerイメージを使うという選択肢があることに気がついた。

Docker 上で実行

1.20.0 タグの付いたイメージを使って、コンテナ上で実行すればいいじゃない!

docker run して、

docker run -itd --name golang1.20.0 golang:1.20.0-alpine3.16

コンテナ上でmain.goを作る。

docker exec -it golang1.20.0 /bin/sh

# 以下はコンテナ上で実行
vi main.go
package main

import (
    "math/rand"
)

func main() {
    println(rand.Intn(10))
}

何度か実行してみる。

go run main.go
2
go run main.go
7
go run main.go
0
go run main.go
4
go run main.go
6
go run main.go
5

ちゃんと値がばらけている。

タネを生成したいときはどうするの

rand.New で生成できるみたい。

ドキュメントはちゃんと読めていないが、Exampleをもとにプログラムも書いてみた。

package main

import (
    "math/rand"
)

func main() {
    r := rand.New(rand.NewSource(99))
    println(r.Intn(10))
}

実行してみると固定値が返る。

go run main.go
7
go run main.go
7
go run main.go
7
go run main.go
7

ちなみに rand.Seed は非推奨になっている。

確かにグローバルに書き換わるのは安心して使えないので、スコープの閉じた生成器を用意するのが良いよね。

そういえば

Goを書きたての時に、タネが固定値であることを知らず、何度実行しても同じ値が返ってくる!とハマった気がする。

あの時の自分のように悩む人が今後はいなくなりますね。

参考