Closed kushuh closed 2 years ago
I even thought of another nice addition while writing this proposal, but it might be extra complicated for nothing (that's why I'm sparing the main message).
The idea is to not be limited by single runes, but allow to basically pick up words (it can be used for name generators, or user-friendly urls ?).
package main
import (
"math/rand"
)
func main() {
id := rand.StringDictionary(dic, 3) // fish-joystick-robot
}
I believe using dictionaries may only prove interesting if you have a very large collection, so just using a Go variable may be harmless in this case. Maybe make dictionary an interface that returns a numerically indexed element ?
type Dictionary interface{
Size() int64
Pick(pos int64) string
}
So that knowing the total size of the dictionary, the rand method could pick a random position and ask for the element located here. Dictionary could wrap anything like a database where the whole elements would be stored. I also don't think this particular addition needs a crypto/rand
implementation, math/rand
may be sufficient.
Nit picking but your UUID implementation is not up to RFC4122.
0b10
.Example in the github.com/google/uuid
lib:
https://github.com/google/uuid/blob/44b5fee7c49cf3bcdf723f106b36d56ef13ccc88/version4.go#L53-L54
Nit picking but your UUID implementation is not up to RFC4122.
Yes I gave it as a (probably bad) example of what using custom alphabets could look like.
I think the String
method alone is sufficient to cover most use-cases, but having the possibility to build from a custom set of runes sounds nice.
StringAlphabet looks questionable, what happens if I put a multi codepoint character in there, like an emoji requiring joiners?
as for simple strings, as pointed out above, uuids actually have required set bits, while in other cases it's simple enough to pass the output of rand.Read through a hex/base32/base64 encoding to get a "safe" string
@seankhliao Maybe StringAlphabet
could accept a []byte
or []rune
argument, instead of a string. This would prevent multi-codepoints characters, and strings can be easily converted:
package main
import (
"math/rand"
)
var alphabet = []rune("1234567890")
func main() {
id := rand.StringAlphabet(alphabet, 10) // 2834753819
}
it's simple enough to pass the output of rand.Read through a hex/base32/base64 encoding to get a "safe" string
I did not knew about this solution, although it seems less flexible and straightforward than having a string method where you explicitly control the size of the output.
I did not knew about this solution, although it seems less flexible and straightforward than having a string method where you explicitly control the size of the output.
There is plenty if solutions already:
io.ReadFull
io.LimitedReader
Perhaps to be really flexible:
// Choice randomly picks items from input with replacement and copies them to output.
func Choice[T any](input, output []T) {
...
}
Then you can pass a []byte
/[]rune
to generate a random string, or a []string
to generate a random "sentence" (and use the existing strings.Join
on the result), or so on.
I called it Choice
because of numpy's random.choice
but a different name would also be fine as long as it is clear.
It seems like this could be done in a separate package outside the standard library. Being able to say exactly what kind of random string you want is a lot of API, since different use cases will want different kinds of strings.
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
A data point: a middle-sized system (200k LOC Go) that talks to dozens of other system. There is a function in a shared private library similar to the one proposed here. It is used 3 times, and around 20 other variants of getting a random string are spread around the codebase, as the requirements for these strings are peculiar, and can't be easily expressed declaratively. We're going to remove library function.
Based on the discussion above, this proposal seems like a likely decline. — rsc for the proposal review group
@kushuh, if you still need this functionality, try out my random package:
It is made to have deterministic randoms during tests, but the package itself doesn't depend on the testing
package.
It can also be used with a Crypto random seed.
package main
import (
"math/rand"
"time"
"github.com/adamluzsi/testcase/random"
)
func main() {
rnd := random.New(rand.NewSource(time.Now().Unix())) // or random.New(random.CryptoSeed{})
_ = rnd.StringNC(42, random.CharsetAlpha())
}
The issue
There are use-cases where we need or could want to generate a random string:
For now neither
math/rand
norcrypto/rand
provide a straightforward solution.Proposal
Add a
String
function to any/both of the rand packages, with only a length parameter to generate a random string of any length.The method would only take a size parameter, that would determine the length of the final string (and possibly panic if this size is negative or too big?).
I think it should also generate a url-safe string, or even only alphanumerics/latin alphabet characters.
StringAlphabet
For a greater coverage, we could also include a more sophisticated method, that would accept an "alphabet" (a user generated set of elements to pick up as runes). Maybe the method could be named
StringAlphabet
? (I'm not the best at naming xD)Where the alphabet argument would be a string of allowed runes to pick up. Could be used for example to generate uuids, where alphabet would look like
"0123456789abcdef"
.Conclusion
I use some working examples I wrote in my packages, so the concept seems to work. However I don't know anything about pseudo-random generators and my solution may be far from optimized.
For the
String
method at least, I found this stackoverflow post that provides a very pleasant solution.