feat: add unbiased integer range helpers

Expand range sampling from the original u32-only helper to all primitive
integer widths. The new gen_range_<int> methods cover 0..n sampling, while
matching gen_range_<int>_in methods accept standard RangeBounds forms such as
exclusive, inclusive, open-ended, and full-width ranges.

Use rejection sampling against the matching primitive reader for each integer
width. That keeps modulo bias out of the public helpers without introducing a
general RNG trait or depending on rand. Full-width ranges fall back directly to
the primitive reader because their span cannot be represented in the same
integer type.

Document the generated APIs with doctested examples and add a ranges example
that demonstrates unsigned, signed, usize, and full-width sampling.

Test Plan:
- cargo test
- cargo clippy
- cargo clippy --benches
- cargo clippy --tests
- cargo +nightly fmt

Refs: IDEAS.md ergonomics backlog
This commit is contained in:
2026-04-28 20:03:37 +02:00
parent 733a2508cb
commit b8f9fa9b1b
3 changed files with 263 additions and 22 deletions
+21
View File
@@ -0,0 +1,21 @@
//! Run with: `cargo run --example ranges`
use ez_urandom::OsRandom;
fn main() -> std::io::Result<()> {
let mut rng = OsRandom::try_new()?;
let byte = rng.gen_range_u8(10)?;
let port = rng.gen_range_u16_in(49152..=65535)?;
let index = rng.gen_range_usize_in(0..8)?;
let offset = rng.gen_range_i32_in(-10..=10)?;
let full_width = rng.gen_range_i128_in(..)?;
println!("u8 below 10 : {byte}");
println!("ephemeral port : {port}");
println!("usize index : {index}");
println!("signed offset : {offset}");
println!("full-width i128 : {full_width}");
Ok(())
}