Hi, I would like to generate a random number (u8) between 0..=15, but with certain numbers (e.g. 0 and 2) excluded (i.e. they will never be generated). What is the cleanest/most idiomatic way to do this? I thought maybe there is a way I can express this sort of range (0..=15 turns into 1..=1 and 3..=15 somehow, which is a range) idiomatically. Is that possible?

Discontinuous ranges aren't a thing in Rust. I can think of two ways of doing this:

- Generate on
`0..16`

and retry if you get an excluded value. - Generate
`x ∈ 0..14`

then add 1 if`x >= 0`

, then add 1 if`x >= 2`

(*i.e.*create the gaps after generation).

I'd probably do the second, as it should always take about the same amount of time for each sample, whereas 1 could require a theoretically unbounded number of attempts.

An idea that just popped into my mind: I could store all possible values in a vector, and then generate a random number that just represents the index into that vector. What do you think?

Whichever algorithm you end up using, the most idiomatic way to implement it is probably to make your own type that implements `Distribution<T>`

, so that it can be used with `Rng::sample()`

. For example:

```
use rand::{
self,
distributions::{Distribution, Uniform},
thread_rng, Rng,
};
struct Filter<Dist, Test> {
dist: Dist,
test: Test,
}
impl<T, Dist, Test> Distribution<T> for Filter<Dist, Test>
where
Dist: Distribution<T>,
Test: Fn(&T) -> bool,
{
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> T {
loop {
let x = self.dist.sample(rng);
if (self.test)(&x) {
return x;
}
}
}
}
fn main() {
let mut rng = thread_rng();
let dist = Filter {
dist: Uniform::new(0, 15),
test: |x: &_| (x != &0) & (x != &2),
};
for _ in 0..32 {
let x: i32 = rng.sample(&dist);
dbg!(x);
}
}
```

*Edit:* Ran through Rustfmt

That was my first idea when reading your question. I just would use an array and not a vec

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.