I'd like to propose 2 minor additions to RandomNumberGenerator
:
1. Filling buffers
Currently, RandomNumberGenerator
has a single requirement, which returns a UInt64
of random bits. This is despite the fact that the runtime function, swift_stdlib_random
is able to fill a buffer with an arbitrary number of random bytes.
All of the sources of randomness: arc4random_buf
, BCryptGenRandom
, the Linux getrandom
syscall, the BSD getentropy
syscall, and reading from /dev/urandom
, all support filling arbitrary-sized buffers with random bytes.
By limiting RandomNumberGenerator
, we make it so that code which needs more than 64 bits of randomness requires multiple syscalls, and is more expensive than calling the above functions (or even the standard library's own runtime function) directly.
So I propose we add a requirement which does that:
protocol RandomNumberGenerator {
// Existing API
mutating func next() -> UInt64
// Added:
mutating func next(into buffer: UnsafeMutableRawBufferPointer)
}
This function will have a trivial default implementation, which simply fills the buffer using bytes from the existing UInt64
requirement.
2. Static member syntax
We now have static member syntax for common protocol conformances. We should use it for SystemRandomNumberGenerator
:
extension RandomNumberGenerator where Self == SystemRandomNumberGenerator {
static var system: Self {
get { SystemRandomNumberGenerator() }
set { /* No-op, throwaway instance. */ }
}
}
If you're adding support for generating random values to custom types, it's quite common to add 2 overloads: one with a user-specified RNG, and one which uses SystemRandomNumberGenerator
. This change results in a very slight QoL improvement when writing those overloads:
extension MyType {
static func random() -> Self {
random(using: &.system)
}
// Used to be:
// static func random() -> Self {
// var rng = SystemRandomNumberGenerator()
// random(using: &rng)
// }
static func random<RNG>(using rng: inout RNG) -> Self where RNG: RandomNumberGenerator {
/* ... */
}
}