Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: math/rand: make global Seed a no-op #67273

Open
rsc opened this issue May 9, 2024 · 3 comments
Open

proposal: math/rand: make global Seed a no-op #67273

rsc opened this issue May 9, 2024 · 3 comments
Labels
Milestone

Comments

@rsc
Copy link
Contributor

rsc commented May 9, 2024

In Go 1.24, I propose to make rand.Seed (the top-level function) a no-op, controlled by GODEBUG=randseednop=1. (Because it is GODEBUG-controlled, specific programs could opt back to the old behavior using a //go:debug line, through Go 1.27 at least.)

The rationale is that rand.Seed is already deprecated, but it is preventing math/rand users from getting more secure randomness by default. There exist imported packages that call rand.Seed(time.Now().UnixNano()) “just in case”, and that makes math/rand fall back to the old Go 1 generator.

There no doubt exist some programs that use rand.Seed correctly for a reproducible simulation or the like, with only one goroutine at a time using the randomness, and being careful not to call any standard library or other packages that themselves contain even a single call to rand.Int or any other top-level function. It is possible, but it is fragile. (This is the same argument we made to auto-seed and deprecate Seed in the first place.)

Those programs can do what we said back then: instead of rand.Seed(s), use r := rand.New(rand.NewSource(s)) and pass r around. They can even name the variable rand to minimize code updates. They can even:

import mathrand "math/rand"
var rand = mathrand.New(mathrand.NewSource(seed))

Making those already-fragile programs convert to this form will remove the fragility, and it will let us fix the majority case of programs that don't care about reproducibility but are getting the insecure Go 1 generator when they could instead be getting the secure ChaCha8 generator.

If Go 1.28 retired the GODEBUG (following our “minimum four release” policy for GODEBUGs), then at that point we could make math/rand's top-level functions trivial wrappers around math/rand/v2's top-level functions, making them eligible for gofix to update. (Note that Go 1.28 would not arrive until the year 2027 - this is very much playing the long game.)

@gopherbot gopherbot added this to the Proposal milestone May 9, 2024
@apparentlymart
Copy link

I am tangentially responsible for a codebase that does use fixed seeding to produce reproducible random numbers, and so I was initially concerned by this proposal, but I see that it's talking specifically about the global rand.Seed and would not affect the ability to instantiate a source with a specified seed.

That seems very reasonable to me. Expecting the shared global source to have a predictable seed, even though literally anything in the program could reseed it at any time, seems like an unreasonable expectation.


The current docs for rand.Seed promise that one can call rand.Seed(1) to achieve behavior from older versions of Go. Would that promise somehow be kept, e.g. by treating the argument 1 differently than other values? Or do you think it's defensible to break that contract? (I don't have any strong opinion either way)

Prior to Go 1.20, the generator was seeded like Seed(1) at program startup. To force the old behavior, call Seed(1) at program startup. Alternately, set GODEBUG=randautoseed=0 in the environment before making any calls to functions in this package.

@rsc rsc changed the title proposal: math/rand: make Seed a no-op proposal: math/rand: make global Seed a no-op May 9, 2024
@rsc
Copy link
Contributor Author

rsc commented May 9, 2024

I am tangentially responsible for a codebase that does use fixed seeding to produce reproducible random numbers, and so I was initially concerned by this proposal, but I see that it's talking specifically about the global rand.Seed and would not affect the ability to instantiate a source with a specified seed.

Indeed. I added "global" to the title. Thanks for pointing out the potential confusion.

The current docs for rand.Seed promise that one can call rand.Seed(1) to achieve behavior from older versions of Go. Would that promise somehow be kept, e.g. by treating the argument 1 differently than other values? Or do you think it's defensible to break that contract? (I don't have any strong opinion either way)

No, the promise would be broken, just with a 2+ year grace period. We already broke the promise for what happens when you don't seed, so it's the same promise, broken twice.

@rsc
Copy link
Contributor Author

rsc commented May 15, 2024

This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.
— rsc for the proposal review group

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Active
Development

No branches or pull requests

3 participants