Skip to content

Commit 1e9be1b

Browse files
committed
Auto merge of #151891 - jhpratt:rollup-cG0AjgK, r=jhpratt
Rollup of 5 pull requests Successful merges: - #143650 (core: add Option::get_or_try_insert_with) - #151726 (Remove duplicated code in `slice/index.rs`) - #151812 (Add `shift_{left,right}` on slices) - #151829 (Implement `BinaryHeap::pop_if()`) - #151838 (Fix typo for Maybe dangling docs)
2 parents 310ae8c + 4b850fa commit 1e9be1b

10 files changed

Lines changed: 344 additions & 41 deletions

File tree

library/alloc/src/collections/binary_heap/mod.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,33 @@ impl<T: Ord, A: Allocator> BinaryHeap<T, A> {
649649
})
650650
}
651651

652+
/// Removes and returns the greatest item from the binary heap if the predicate
653+
/// returns `true`, or [`None`] if the predicate returns false or the heap
654+
/// is empty (the predicate will not be called in that case).
655+
///
656+
/// # Examples
657+
///
658+
/// ```
659+
/// #![feature(binary_heap_pop_if)]
660+
/// use std::collections::BinaryHeap;
661+
/// let mut heap = BinaryHeap::from([1, 2]);
662+
/// let pred = |x: &i32| *x % 2 == 0;
663+
///
664+
/// assert_eq!(heap.pop_if(pred), Some(2));
665+
/// assert_eq!(heap.as_slice(), [1]);
666+
/// assert_eq!(heap.pop_if(pred), None);
667+
/// assert_eq!(heap.as_slice(), [1]);
668+
/// ```
669+
///
670+
/// # Time complexity
671+
///
672+
/// The worst case cost of `pop_if` on a heap containing *n* elements is *O*(log(*n*)).
673+
#[unstable(feature = "binary_heap_pop_if", issue = "151828")]
674+
pub fn pop_if(&mut self, predicate: impl FnOnce(&T) -> bool) -> Option<T> {
675+
let first = self.peek()?;
676+
if predicate(first) { self.pop() } else { None }
677+
}
678+
652679
/// Pushes an item onto the binary heap.
653680
///
654681
/// # Examples

library/alloctests/tests/collections/binary_heap.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,18 @@ fn test_peek_and_pop() {
136136
}
137137
}
138138

139+
#[test]
140+
fn test_pop_if() {
141+
let data = vec![9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
142+
let mut sorted = data.clone();
143+
sorted.sort();
144+
let mut heap = BinaryHeap::from(data);
145+
while let Some(popped) = heap.pop_if(|x| *x > 2) {
146+
assert_eq!(popped, sorted.pop().unwrap());
147+
}
148+
assert_eq!(heap.into_sorted_vec(), vec![0, 1, 2]);
149+
}
150+
139151
#[test]
140152
fn test_peek_mut() {
141153
let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];

library/alloctests/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#![feature(allocator_api)]
2+
#![feature(binary_heap_pop_if)]
23
#![feature(const_heap)]
34
#![feature(deque_extend_front)]
45
#![feature(iter_array_chunks)]

library/core/src/mem/maybe_dangling.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::{mem, ptr};
2929
/// mem::forget(boxed); // <-- this is UB!
3030
/// ```
3131
///
32-
/// Even though the `Box`e's destructor is not run (and thus we don't have a double free bug), this
32+
/// Even though the `Box`'s destructor is not run (and thus we don't have a double free bug), this
3333
/// code is still UB. This is because when moving `boxed` into `forget`, its validity invariants
3434
/// are asserted, causing UB since the `Box` is dangling. The safety comment is as such wrong, as
3535
/// moving the `boxed` variable as part of the `forget` call *is* a use.

library/core/src/ops/range.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@ impl<T: Copy> Bound<&T> {
773773
/// ```
774774
#[unstable(feature = "bound_copied", issue = "145966")]
775775
#[must_use]
776-
pub fn copied(self) -> Bound<T> {
776+
pub const fn copied(self) -> Bound<T> {
777777
match self {
778778
Bound::Unbounded => Bound::Unbounded,
779779
Bound::Included(x) => Bound::Included(*x),

library/core/src/option.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@
584584
use crate::clone::TrivialClone;
585585
use crate::iter::{self, FusedIterator, TrustedLen};
586586
use crate::marker::Destruct;
587-
use crate::ops::{self, ControlFlow, Deref, DerefMut};
587+
use crate::ops::{self, ControlFlow, Deref, DerefMut, Residual, Try};
588588
use crate::panicking::{panic, panic_display};
589589
use crate::pin::Pin;
590590
use crate::{cmp, convert, hint, mem, slice};
@@ -1816,6 +1816,49 @@ impl<T> Option<T> {
18161816
unsafe { self.as_mut().unwrap_unchecked() }
18171817
}
18181818

1819+
/// If the option is `None`, calls the closure and inserts its output if successful.
1820+
///
1821+
/// If the closure returns a residual value such as `Err` or `None`,
1822+
/// that residual value is returned and nothing is inserted.
1823+
///
1824+
/// If the option is `Some`, nothing is inserted.
1825+
///
1826+
/// Unless a residual is returned, a mutable reference to the value
1827+
/// of the option will be output.
1828+
///
1829+
/// # Examples
1830+
///
1831+
/// ```
1832+
/// #![feature(option_get_or_try_insert_with)]
1833+
/// let mut o1: Option<u32> = None;
1834+
/// let mut o2: Option<u8> = None;
1835+
///
1836+
/// let number = "12345";
1837+
///
1838+
/// assert_eq!(o1.get_or_try_insert_with(|| number.parse()).copied(), Ok(12345));
1839+
/// assert!(o2.get_or_try_insert_with(|| number.parse()).is_err());
1840+
/// assert_eq!(o1, Some(12345));
1841+
/// assert_eq!(o2, None);
1842+
/// ```
1843+
#[inline]
1844+
#[unstable(feature = "option_get_or_try_insert_with", issue = "143648")]
1845+
pub fn get_or_try_insert_with<'a, R, F>(
1846+
&'a mut self,
1847+
f: F,
1848+
) -> <R::Residual as Residual<&'a mut T>>::TryType
1849+
where
1850+
F: FnOnce() -> R,
1851+
R: Try<Output = T, Residual: Residual<&'a mut T>>,
1852+
{
1853+
if let None = self {
1854+
*self = Some(f()?);
1855+
}
1856+
// SAFETY: a `None` variant for `self` would have been replaced by a `Some`
1857+
// variant in the code above.
1858+
1859+
Try::from_output(unsafe { self.as_mut().unwrap_unchecked() })
1860+
}
1861+
18191862
/////////////////////////////////////////////////////////////////////////
18201863
// Misc
18211864
/////////////////////////////////////////////////////////////////////////

library/core/src/slice/index.rs

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -910,29 +910,7 @@ where
910910
R: [const] ops::RangeBounds<usize> + [const] Destruct,
911911
{
912912
let len = bounds.end;
913-
914-
let end = match range.end_bound() {
915-
ops::Bound::Included(&end) if end >= len => slice_index_fail(0, end, len),
916-
// Cannot overflow because `end < len` implies `end < usize::MAX`.
917-
ops::Bound::Included(&end) => end + 1,
918-
919-
ops::Bound::Excluded(&end) if end > len => slice_index_fail(0, end, len),
920-
ops::Bound::Excluded(&end) => end,
921-
ops::Bound::Unbounded => len,
922-
};
923-
924-
let start = match range.start_bound() {
925-
ops::Bound::Excluded(&start) if start >= end => slice_index_fail(start, end, len),
926-
// Cannot overflow because `start < end` implies `start < usize::MAX`.
927-
ops::Bound::Excluded(&start) => start + 1,
928-
929-
ops::Bound::Included(&start) if start > end => slice_index_fail(start, end, len),
930-
ops::Bound::Included(&start) => start,
931-
932-
ops::Bound::Unbounded => 0,
933-
};
934-
935-
ops::Range { start, end }
913+
into_slice_range(len, (range.start_bound().copied(), range.end_bound().copied()))
936914
}
937915

938916
/// Performs bounds checking of a range without panicking.
@@ -972,20 +950,8 @@ where
972950
R: ops::RangeBounds<usize>,
973951
{
974952
let len = bounds.end;
975-
976-
let start = match range.start_bound() {
977-
ops::Bound::Included(&start) => start,
978-
ops::Bound::Excluded(start) => start.checked_add(1)?,
979-
ops::Bound::Unbounded => 0,
980-
};
981-
982-
let end = match range.end_bound() {
983-
ops::Bound::Included(end) => end.checked_add(1)?,
984-
ops::Bound::Excluded(&end) => end,
985-
ops::Bound::Unbounded => len,
986-
};
987-
988-
if start > end || end > len { None } else { Some(ops::Range { start, end }) }
953+
let r = into_range(len, (range.start_bound().copied(), range.end_bound().copied()))?;
954+
if r.start > r.end || r.end > len { None } else { Some(r) }
989955
}
990956

991957
/// Converts a pair of `ops::Bound`s into `ops::Range` without performing any
@@ -1011,6 +977,7 @@ pub(crate) const fn into_range_unchecked(
1011977
/// Converts pair of `ops::Bound`s into `ops::Range`.
1012978
/// Returns `None` on overflowing indices.
1013979
#[rustc_const_unstable(feature = "const_range", issue = "none")]
980+
#[inline]
1014981
pub(crate) const fn into_range(
1015982
len: usize,
1016983
(start, end): (ops::Bound<usize>, ops::Bound<usize>),
@@ -1036,7 +1003,8 @@ pub(crate) const fn into_range(
10361003

10371004
/// Converts pair of `ops::Bound`s into `ops::Range`.
10381005
/// Panics on overflowing indices.
1039-
pub(crate) fn into_slice_range(
1006+
#[inline]
1007+
pub(crate) const fn into_slice_range(
10401008
len: usize,
10411009
(start, end): (ops::Bound<usize>, ops::Bound<usize>),
10421010
) -> ops::Range<usize> {

0 commit comments

Comments
 (0)