Skip to content

Convenient PRNG construction: seed_from_u64 / from_hashable #522

Closed
@dhardy

Description

@dhardy

History: when discussing the design of the SeedableRng trait (dhardy#18), we came up with a number of proposals, before settling on the current design. However we always intended on adding another function for convenient generic construction.

Motivation: provide a convenient way to seed any PRNGs via a trait function. This is primarily aimed at users wanting to construct reproducible PRNGs for games and simulations, and is not for password hashing.

Requirements

  • easy to use
  • suitable for generic code
  • allow at least 64-bits of input state/entropy

And non-requirements:

  • performance: we don't expect users to construct large numbers of PRNGs using this mechanism
  • security: this mechanism is not intended for seeding secure PRNGs

Proposals

  • fn seed_from_u64(&mut self, seed: u64) -> Self

    This function is proposed as the simplest option fulfilling all requirements. Quoting @pitdicker (Seeding PRNGs (traits and bit size) dhardy/rand#18 (comment)):

    I think again that a seed_from_u64 function is convenient (from_seed is the 'right' function, but not all that easy to use). Simple deterministic initialization for tests, simulations etc. is something we do not support all that well yet.

    For cryptographic RNGs the u64 can simply be mapped to the first 8 bytes of the seed. For small RNGs small numbers or patterns can start the RNG in a weak state. But the common way I have seen that handled is to just run the RNG the number of times it approximately takes to escape a weak state, usually about 20 times.

    So I would propose to add seed_from_u64 to the SeedableRng trait, with a default implementation. The default implementation will repeat the u64 to fill the seed, initialize the RNG with from_seed, and call next_u32 20 times.

    The advantage of adding the function to SeedableRng is that RNG implementations can override it if for example the number of times to call next_u32 is less (and 0 for cryptographic RNGs).

  • fn from_hashable<T: Hashable>(x: T) -> Self: SeedableRng::Seed and from_hashable dhardy/rand#62; code

    This proposal is very flexible (any PRNG can be constructed from any hashable type), but involves adding a hash function to rand_core (or potentially rand, but this prevents from_hashable being defined on SeedableRng), as well as questions of whether we should use a cryptographic hash function or optimise for speed (neither appears necessary).

    Note that we wish to allow users to reproduce output from PRNGs constructed via this mechanism, thus we cannot rely on std::hash (which is not portable and whose implementation may change).

  • No extra functionality. SeedableRng::Seed already supports Default and AsMut<[u8]>, i.e. it can be constructed with default() and then accessed as &mut [u8]. This is sufficient to construct arbitrary seeds using generic code, but not necessarily convenient.

Note: whatever we go with, it is possible for both of these proposals to be implemented by an external crate, with the only restriction being that this function cannot be added to the SeedableRng trait.


Currently I like @pitdicker's recent suggestion best: add SeedableRng::seed_from_u64 with a default implementation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    E-questionParticipation: opinions wantedF-new-intFunctionality: new, within Rand

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions