Generating random `usize`s; is there a footgun here I'm not considering?

To generate a random usize, I wrote this function:

use rand::Rng;

fn random_usize<R: Rng>(rng: &mut R) -> usize {
    #[cfg(target_pointer_width = "64")]
    let bytes: [u8; 8] = rng.random();

    #[cfg(target_pointer_width = "32")]
    let bytes: [u8; 4] = rng.random();

    #[cfg(target_pointer_width = "16")]
    let bytes: [u8; 2] = rng.random();

    usize::from_ne_bytes(bytes)
}

I chose to use usize::from_ne_bytes instead of using TryFrom::try_from or as casts because I would like to avoid:

  • .expect("Infallible")
  • panics from accidental misuse (ie, lazy copy pasting of cfg attributes and forgetting to change the pointer width)
  • silent truncation
  • infecting code with Results for an operation that, to my understanding, should be infallible (given it is gated behind cfg attributes for correctness)

...and any other form of incorrectness I might be forgetting here.

From what I can gather by messing around on Compiler Explorer, it's a compile-time error if I were to get the number of bytes wrong or forget to implement the pointer-width of the target. Are there any footguns I'm failing to consider here? Given what I'm after, is there a better way to do this (ie, one where I won't forget a pointer-width even if it isn't relevant to my current platform, such as some sort of exhaustive match on target_pointer_width)?

Any insight is appreciated.

This looks fine. However, you can simplify it by using type inference:

pub fn random_usize<R: Rng>(rng: &mut R) -> usize {
    usize::from_ne_bytes(rng.random())
}
5 Likes

It hadn't occurred to me that type inference would cover this. This seems like a better way to handle it, thanks!

Note the potential portability hazard: you will get different results on 32 and 64 bit targets. It's fine if you use a nondeterministic RNG, but it could be a problem with a deterministically seeded PRNG.

Also, using rng.next_u32/64() could be more efficient depending on used PRNG.

1 Like