rust-random / rand

A Rust library for random number generation.
https://crates.io/crates/rand
Other
1.68k stars 430 forks source link

SeekableRng trait for Rngs with a block_pos? #1375

Open nstilt1 opened 10 months ago

nstilt1 commented 10 months ago

Background

1369 has brought up a use for set_block_pos() and get_block_pos() for rand_chacha.

In implementing it for chacha20 with BlockRng, there is a minor issue with the API without using a new trait. The methods seem like they should belong on the core instead of the rng, but when implementing just those 2 methods, it results in these calls:

// this is essentially how the Rng is defined in this scenario
pub struct ChaCha20Rng {
  pub rng: BlockRng<ChaCha20Core>,
}

let mut rng = ChaCha20Rng::from_entropy();
rng.rng.core.set_block_pos(x);
rng.rng.core.get_block_pos();

This could be simplified by adding a ChaCha20Rng::get_core_mut() method, down to:

rng.get_core_mut().set_block_pos(x);

But now there are even more characters to type.

There is also a potential issue regarding set_block_pos() located on the core. The following code will fail because changing the block_pos on the core will not affect the index of the rng. So we will need to decide if that is okay or not:

let mut rng = ChaCha20Rng::from_entropy();

rng.core.set_block_pos(5);
let a = rng.next_u32();

rng.core.set_block_pos(5);
let b = rng.next_u32();

// this would fail
assert_eq!(a, b);

A SeekableRng trait might suffice, allowing for a shorter method call, and it could also handle the above issue depending on if that is an issue that needs to be fixed.

Feature request

I don't know if it would be possible to make a simplified call such as this:

let mut rng = ChaCha20Rng::from_entropy();
rng.core.set_block_pos();

But it seems like for that method to be called that way, SeekableRng would need to be implemented for BlockRng if the rng is a wrapper around it. The method does not need to be called that way though. I personally don't mind whether it is on the rng or the core, but it just seems like something that would go on the core.

Proposed trait definition:

pub trait SeekableRng {
  type Counter;
  fn set_block_pos(block_pos: Counter);
  fn get_block_pos() -> Counter;
}
dhardy commented 10 months ago
struct ChaCha20Rng(BlockRng<ChaCha20Core>);
impl ChaCha20Rng {
    fn set_block_pos(x: _) {
        self.0.core.set_block_pos(x);
        self.0.reset(); // or generate_and_set(index)
    }
}

This should be enough for a == b. I don't think we need any impls directly on BlockRng.

ShonFrazier commented 10 months ago

Definitely an important feature for verification. For example, when one needs to replay an RNG.

dhardy commented 4 months ago

Related: #1458.