-
Notifications
You must be signed in to change notification settings - Fork 13.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement iterator specialization traits on more adapters #85528
Changes from all commits
a2a7caa
451a3b1
3aa7313
37d26c7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,10 @@ | ||
use crate::intrinsics::unlikely; | ||
use crate::iter::adapters::zip::try_get_unchecked; | ||
use crate::iter::TrustedFused; | ||
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable}; | ||
use crate::iter::{ | ||
adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedLen, TrustedRandomAccess, | ||
TrustedRandomAccessNoCoerce, | ||
}; | ||
use crate::num::NonZeroUsize; | ||
use crate::ops::{ControlFlow, Try}; | ||
|
||
|
@@ -152,6 +156,32 @@ where | |
|
||
NonZeroUsize::new(n).map_or(Ok(()), Err) | ||
} | ||
|
||
#[doc(hidden)] | ||
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item | ||
where | ||
Self: TrustedRandomAccessNoCoerce, | ||
{ | ||
// SAFETY: the caller must uphold the contract for | ||
// `Iterator::__iterator_get_unchecked`. | ||
// | ||
// Dropping the skipped prefix when index 0 is passed is safe | ||
// since | ||
// * the caller passing index 0 means that the inner iterator has more items than `self.n` | ||
// * TRA contract requires that get_unchecked will only be called once | ||
// (unless elements are copyable) | ||
// * it does not conflict with in-place iteration since index 0 must be accessed | ||
// before something is written into the storage used by the prefix | ||
unsafe { | ||
if Self::MAY_HAVE_SIDE_EFFECT && idx == 0 { | ||
for skipped_idx in 0..self.n { | ||
drop(try_get_unchecked(&mut self.iter, skipped_idx)); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In light of #85969 I must note that this alters (undocumented) side-effects for backwards iteration, albeit in less surprising ways. This block is written to preserve side-effects of the skipped portion of the iterator during forward iteration. During backward iteration it will lead to the whole skipped portion being drained on the last (backwards) step instead of only the first item beyond the skipped portion, i.e. it behaves as if the last item were obtained by a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, so, my interpretation of what you're saying: we're picking But I think my interpretation is probably wrong -- I've tried several times and I cannot manage to find a case where your patch causes different behavior in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @the8472 Okay, this is a slight behavioral change to behavior we never made a promise about. I think it's a change we're allowed to make, but to be safe, that decision should be left to t-libs-api FCP. Do you mind writing up the details about before/after differences so that that can be done? |
||
} | ||
|
||
try_get_unchecked(&mut self.iter, idx + self.n) | ||
} | ||
} | ||
} | ||
|
||
#[stable(feature = "rust1", since = "1.0.0")] | ||
|
@@ -237,3 +267,23 @@ unsafe impl<I: InPlaceIterable> InPlaceIterable for Skip<I> { | |
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY; | ||
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY; | ||
} | ||
|
||
#[doc(hidden)] | ||
#[unstable(feature = "trusted_random_access", issue = "none")] | ||
unsafe impl<I> TrustedRandomAccess for Skip<I> where I: TrustedRandomAccess {} | ||
|
||
#[doc(hidden)] | ||
#[unstable(feature = "trusted_random_access", issue = "none")] | ||
unsafe impl<I> TrustedRandomAccessNoCoerce for Skip<I> | ||
where | ||
I: TrustedRandomAccessNoCoerce, | ||
{ | ||
const MAY_HAVE_SIDE_EFFECT: bool = I::MAY_HAVE_SIDE_EFFECT; | ||
} | ||
|
||
// SAFETY: This adapter is shortening. TrustedLen requires the upper bound to be calculated correctly. | ||
// These requirements can only be satisfied when the upper bound of the inner iterator's upper | ||
// bound is never `None`. I: TrustedRandomAccess happens to provide this guarantee while | ||
// I: TrustedLen would not. | ||
#[unstable(feature = "trusted_len", issue = "37572")] | ||
unsafe impl<I> TrustedLen for Skip<I> where I: Iterator + TrustedRandomAccess {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: IIUC the guarantee for being called once is just once when
idx == 0
, but this kinda reads like it's universally true (even though that makes no sense).