diff --git a/CHANGELOG.md b/CHANGELOG.md index d2f78fa65b..4e0e189f60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ A [separate changelog is kept for rand_core](https://github.com/rust-random/core You may also find the [Upgrade Guide](https://rust-random.github.io/book/update.html) useful. +## [Unreleased] + +### Changes +- Document required output order of fn `partial_shuffle` and apply `#[must_use]` ([#1769]) + +[#1769]: https://github.com/rust-random/rand/pull/1769 + ## [0.10.1] — 2026-02-11 This release includes a fix for a soundness bug; see [#1763]. @@ -1156,7 +1163,7 @@ Code replaced with a compatibility layer over rand 0.4. ### Added - Separate `rand` out of the standard library -[Unreleased]: https://github.com/rust-random/rand/compare/0.10.0...HEAD +[Unreleased]: https://github.com/rust-random/rand/compare/0.10.1...HEAD [0.10.1]: https://github.com/rust-random/rand/compare/0.10.0...0.10.1 [0.10.0]: https://github.com/rust-random/rand/compare/0.9.2...0.10.0 [0.9.2]: https://github.com/rust-random/rand/compare/0.9.1...0.9.2 diff --git a/benches/benches/shuffle.rs b/benches/benches/shuffle.rs index 8ace45a5a1..ec9cab4017 100644 --- a/benches/benches/shuffle.rs +++ b/benches/benches/shuffle.rs @@ -52,7 +52,7 @@ fn bench_rng(c: &mut Criterion, rng_name: &'static str) { let mut rng = R::seed_from_u64(123); let mut vec: Vec = (0..length).collect(); b.iter(|| { - vec.partial_shuffle(&mut rng, length / 2); + let _ = vec.partial_shuffle(&mut rng, length / 2); vec[0] }) }); diff --git a/src/seq/slice.rs b/src/seq/slice.rs index 1b8a068eae..53b91faf4c 100644 --- a/src/seq/slice.rs +++ b/src/seq/slice.rs @@ -420,23 +420,35 @@ pub trait SliceRandom: IndexedMutRandom { where R: Rng + ?Sized; - /// Shuffle a slice in place, but exit early. + /// Sample `amount` shuffled elements /// - /// Returns two mutable slices from the source slice. The first contains - /// `amount` elements randomly permuted. The second has the remaining - /// elements that are not fully shuffled. + /// Shuffles `amount` random elements into the end of the slice (`n..` where + /// `n = self.len() - amount`). The rest of the slice (`..n`) contains the + /// remaining elements in a permuted but not fully shuffled order. + /// + /// Returns a tuple of the sampled elements (`&mut self[n..]`) and the + /// remaining elements (`&mut self[..n]`). /// /// This is an efficient method to select `amount` elements at random from /// the slice, provided the slice may be mutated. /// - /// If you only need to choose elements randomly and `amount > self.len()/2` - /// then you may improve performance by taking - /// `amount = self.len() - amount` and using only the second slice. + /// For slices, complexity is `O(m)` where `m = amount`. + /// If `amount >= self.len()` this is equivalent to [`Self::shuffle`]. /// - /// If `amount` is greater than the number of elements in the slice, this - /// will perform a full shuffle. + /// # Example /// - /// For slices, complexity is `O(m)` where `m = amount`. + /// ``` + /// use rand::seq::SliceRandom; + /// + /// let mut rng = rand::rng(); + /// let mut y = [1, 2, 3, 4, 5]; + /// let (shuffled, rest) = y.partial_shuffle(&mut rng, 3); + /// assert_eq!(shuffled.len(), 3); + /// assert_eq!(rest.len(), 2); + /// let sampled = shuffled.to_vec(); + /// assert_eq!(&sampled, &y[2..5]); + /// ``` + #[must_use] fn partial_shuffle( &mut self, rng: &mut R, @@ -464,14 +476,14 @@ impl SliceRandom for [T] { // There is no need to shuffle an empty or single element slice return; } - self.partial_shuffle(rng, self.len()); + let _ = self.partial_shuffle(rng, self.len()); } fn partial_shuffle(&mut self, rng: &mut R, amount: usize) -> (&mut [T], &mut [T]) where R: Rng + ?Sized, { - let m = self.len().saturating_sub(amount); + let n = self.len().saturating_sub(amount); // The algorithm below is based on Durstenfeld's algorithm for the // [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm) @@ -483,18 +495,18 @@ impl SliceRandom for [T] { // but only works for 32 bit integers // So we must use the slow method if the slice is longer than that. if self.len() < (u32::MAX as usize) { - let mut chooser = IncreasingUniform::new(rng, m as u32); - for i in m..self.len() { + let mut chooser = IncreasingUniform::new(rng, n as u32); + for i in n..self.len() { let index = chooser.next_index(); self.swap(i, index); } } else { - for i in m..self.len() { + for i in n..self.len() { let index = rng.random_range(..i + 1); self.swap(i, index); } } - let r = self.split_at_mut(m); + let r = self.split_at_mut(n); (r.1, r.0) } }