Coverage Report

Created: 2026-06-14 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/rand-0.10.1/src/distr/slice.rs
Line
Count
Source
1
// Copyright 2021 Developers of the Rand project.
2
//
3
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6
// option. This file may not be copied, modified, or distributed
7
// except according to those terms.
8
9
//! Distributions over slices
10
11
use core::num::NonZeroUsize;
12
13
use crate::Rng;
14
use crate::distr::Distribution;
15
use crate::distr::uniform::{UniformSampler, UniformUsize};
16
#[cfg(feature = "alloc")]
17
use alloc::string::String;
18
19
/// A distribution to uniformly sample elements of a slice
20
///
21
/// Like [`IndexedRandom::choose`], this uniformly samples elements of a slice
22
/// without modification of the slice (so called "sampling with replacement").
23
/// This distribution object may be a little faster for repeated sampling (but
24
/// slower for small numbers of samples).
25
///
26
/// ## Examples
27
///
28
/// Since this is a distribution, [`Rng::sample_iter`] and
29
/// [`Distribution::sample_iter`] may be used, for example:
30
/// ```
31
/// use rand::distr::{Distribution, slice::Choose};
32
///
33
/// let vowels = ['a', 'e', 'i', 'o', 'u'];
34
/// let vowels_dist = Choose::new(&vowels).unwrap();
35
///
36
/// // build a string of 10 vowels
37
/// let vowel_string: String = vowels_dist
38
///     .sample_iter(&mut rand::rng())
39
///     .take(10)
40
///     .collect();
41
///
42
/// println!("{}", vowel_string);
43
/// assert_eq!(vowel_string.len(), 10);
44
/// assert!(vowel_string.chars().all(|c| vowels.contains(&c)));
45
/// ```
46
///
47
/// For a single sample, [`IndexedRandom::choose`] may be preferred:
48
/// ```
49
/// use rand::seq::IndexedRandom;
50
///
51
/// let vowels = ['a', 'e', 'i', 'o', 'u'];
52
/// let mut rng = rand::rng();
53
///
54
/// println!("{}", vowels.choose(&mut rng).unwrap());
55
/// ```
56
///
57
/// [`IndexedRandom::choose`]: crate::seq::IndexedRandom::choose
58
/// [`Rng::sample_iter`]: crate::RngExt::sample_iter
59
#[derive(Debug, Clone, Copy)]
60
pub struct Choose<'a, T> {
61
    slice: &'a [T],
62
    range: UniformUsize,
63
    num_choices: NonZeroUsize,
64
}
65
66
impl<'a, T> Choose<'a, T> {
67
    /// Create a new `Choose` instance which samples uniformly from the slice.
68
    ///
69
    /// Returns error [`Empty`] if the slice is empty.
70
0
    pub fn new(slice: &'a [T]) -> Result<Self, Empty> {
71
0
        let num_choices = NonZeroUsize::new(slice.len()).ok_or(Empty)?;
72
73
0
        Ok(Self {
74
0
            slice,
75
0
            range: UniformUsize::new(0, num_choices.get()).unwrap(),
76
0
            num_choices,
77
0
        })
78
0
    }
79
80
    /// Returns the count of choices in this distribution
81
0
    pub fn num_choices(&self) -> NonZeroUsize {
82
0
        self.num_choices
83
0
    }
84
}
85
86
impl<'a, T> Distribution<&'a T> for Choose<'a, T> {
87
0
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> &'a T {
88
0
        let idx = self.range.sample(rng);
89
90
0
        debug_assert!(
91
0
            idx < self.slice.len(),
92
0
            "Uniform::new(0, {}) somehow returned {}",
93
0
            self.slice.len(),
94
            idx
95
        );
96
97
        // Safety: at construction time, it was ensured that the slice was
98
        // non-empty, and that the `Uniform` range produces values in range
99
        // for the slice
100
0
        unsafe { self.slice.get_unchecked(idx) }
101
0
    }
102
}
103
104
/// Error: empty slice
105
///
106
/// This error is returned when [`Choose::new`] is given an empty slice.
107
#[derive(Debug, Clone, Copy)]
108
pub struct Empty;
109
110
impl core::fmt::Display for Empty {
111
0
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112
0
        write!(
113
0
            f,
114
0
            "Tried to create a `rand::distr::slice::Choose` with an empty slice"
115
        )
116
0
    }
117
}
118
119
impl core::error::Error for Empty {}
120
121
#[cfg(feature = "alloc")]
122
impl super::SampleString for Choose<'_, char> {
123
0
    fn append_string<R: Rng + ?Sized>(&self, rng: &mut R, string: &mut String, len: usize) {
124
        // Get the max char length to minimize extra space.
125
        // Limit this check to avoid searching for long slice.
126
0
        let max_char_len = if self.slice.len() < 200 {
127
0
            self.slice
128
0
                .iter()
129
0
                .try_fold(1, |max_len, char| {
130
                    // When the current max_len is 4, the result max_char_len will be 4.
131
0
                    Some(max_len.max(char.len_utf8())).filter(|len| *len < 4)
132
0
                })
133
0
                .unwrap_or(4)
134
        } else {
135
0
            4
136
        };
137
138
        // Split the extension of string to reuse the unused capacities.
139
        // Skip the split for small length or only ascii slice.
140
0
        let mut extend_len = if max_char_len == 1 || len < 100 {
141
0
            len
142
        } else {
143
0
            len / 4
144
        };
145
0
        let mut remain_len = len;
146
0
        while extend_len > 0 {
147
0
            string.reserve(max_char_len * extend_len);
148
0
            string.extend(self.sample_iter(&mut *rng).take(extend_len));
149
0
            remain_len -= extend_len;
150
0
            extend_len = extend_len.min(remain_len);
151
0
        }
152
0
    }
153
}
154
155
#[cfg(test)]
156
mod test {
157
    use super::*;
158
    use core::iter;
159
160
    #[test]
161
    fn value_stability() {
162
        let rng = crate::test::rng(651);
163
        let slice = Choose::new(b"escaped emus explore extensively").unwrap();
164
        let expected = b"eaxee";
165
        assert!(iter::zip(slice.sample_iter(rng), expected).all(|(a, b)| a == b));
166
    }
167
}