| Age | Commit message (Collapse) | Author | Lines |
|
|
|
|
|
Many default iterator methods use `try_fold` or `fold`, and these ones
can too.
|
|
`fold` is currently implemented via `try_fold`, but implementing it
directly results in slightly less LLVM IR being generated, speeding up
compilation of some benchmarks.
(And likewise for `rfold`.)
The commit adds `fold` implementations to all the iterators that lack
one but do have a `try_fold` implementation. Most of these just call the
`try_fold` implementation directly.
|
|
Simpler slice `Iterator` methods
These reduce the amount of LLVM IR generated, helping compile times.
r? @cuviper
|
|
Rework the std::iter::Step trait
Previous attempts: #43127 #62886 #68807
Tracking issue: #42168
This PR reworks the `Step` trait to be phrased in terms of the *successor* and *predecessor* operations. With this, `Step` hopefully has a consistent identity that can have a path towards stabilization. The proposed trait:
```rust
/// Objects that have a notion of *successor* and *predecessor* operations.
///
/// The *successor* operation moves towards values that compare greater.
/// The *predecessor* operation moves towards values that compare lesser.
///
/// # Safety
///
/// This trait is `unsafe` because its implementation must be correct for
/// the safety of `unsafe trait TrustedLen` implementations, and the results
/// of using this trait can otherwise be trusted by `unsafe` code to be correct
/// and fulful the listed obligations.
pub unsafe trait Step: Clone + PartialOrd + Sized {
/// Returns the number of *successor* steps required to get from `start` to `end`.
///
/// Returns `None` if the number of steps would overflow `usize`
/// (or is infinite, or if `end` would never be reached).
///
/// # Invariants
///
/// For any `a`, `b`, and `n`:
///
/// * `steps_between(&a, &b) == Some(n)` if and only if `Step::forward(&a, n) == Some(b)`
/// * `steps_between(&a, &b) == Some(n)` if and only if `Step::backward(&a, n) == Some(a)`
/// * `steps_between(&a, &b) == Some(n)` only if `a <= b`
/// * Corollary: `steps_between(&a, &b) == Some(0)` if and only if `a == b`
/// * Note that `a <= b` does _not_ imply `steps_between(&a, &b) != None`;
/// this is the case wheen it would require more than `usize::MAX` steps to get to `b`
/// * `steps_between(&a, &b) == None` if `a > b`
fn steps_between(start: &Self, end: &Self) -> Option<usize>;
/// Returns the value that would be obtained by taking the *successor*
/// of `self` `count` times.
///
/// If this would overflow the range of values supported by `Self`, returns `None`.
///
/// # Invariants
///
/// For any `a`, `n`, and `m`:
///
/// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, m).and_then(|x| Step::forward_checked(x, n))`
///
/// For any `a`, `n`, and `m` where `n + m` does not overflow:
///
/// * `Step::forward_checked(a, n).and_then(|x| Step::forward_checked(x, m)) == Step::forward_checked(a, n + m)`
///
/// For any `a` and `n`:
///
/// * `Step::forward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::forward_checked(&x, 1))`
/// * Corollary: `Step::forward_checked(&a, 0) == Some(a)`
fn forward_checked(start: Self, count: usize) -> Option<Self>;
/// Returns the value that would be obtained by taking the *successor*
/// of `self` `count` times.
///
/// If this would overflow the range of values supported by `Self`,
/// this function is allowed to panic, wrap, or saturate.
/// The suggested behavior is to panic when debug assertions are enabled,
/// and to wrap or saturate otherwise.
///
/// Unsafe code should not rely on the correctness of behavior after overflow.
///
/// # Invariants
///
/// For any `a`, `n`, and `m`, where no overflow occurs:
///
/// * `Step::forward(Step::forward(a, n), m) == Step::forward(a, n + m)`
///
/// For any `a` and `n`, where no overflow occurs:
///
/// * `Step::forward_checked(a, n) == Some(Step::forward(a, n))`
/// * `Step::forward(a, n) == (0..n).fold(a, |x, _| Step::forward(x, 1))`
/// * Corollary: `Step::forward(a, 0) == a`
/// * `Step::forward(a, n) >= a`
/// * `Step::backward(Step::forward(a, n), n) == a`
fn forward(start: Self, count: usize) -> Self {
Step::forward_checked(start, count).expect("overflow in `Step::forward`")
}
/// Returns the value that would be obtained by taking the *successor*
/// of `self` `count` times.
///
/// # Safety
///
/// It is undefined behavior for this operation to overflow the
/// range of values supported by `Self`. If you cannot guarantee that this
/// will not overflow, use `forward` or `forward_checked` instead.
///
/// # Invariants
///
/// For any `a`:
///
/// * if there exists `b` such that `b > a`, it is safe to call `Step::forward_unchecked(a, 1)`
/// * if there exists `b`, `n` such that `steps_between(&a, &b) == Some(n)`,
/// it is safe to call `Step::forward_unchecked(a, m)` for any `m <= n`.
///
/// For any `a` and `n`, where no overflow occurs:
///
/// * `Step::forward_unchecked(a, n)` is equivalent to `Step::forward(a, n)`
#[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")]
unsafe fn forward_unchecked(start: Self, count: usize) -> Self {
Step::forward(start, count)
}
/// Returns the value that would be obtained by taking the *successor*
/// of `self` `count` times.
///
/// If this would overflow the range of values supported by `Self`, returns `None`.
///
/// # Invariants
///
/// For any `a`, `n`, and `m`:
///
/// * `Step::backward_checked(a, n).and_then(|x| Step::backward_checked(x, m)) == n.checked_add(m).and_then(|x| Step::backward_checked(a, x))`
/// * `Step::backward_checked(a, n).and_then(|x| Step::backward_checked(x, m)) == try { Step::backward_checked(a, n.checked_add(m)?) }`
///
/// For any `a` and `n`:
///
/// * `Step::backward_checked(a, n) == (0..n).try_fold(a, |x, _| Step::backward_checked(&x, 1))`
/// * Corollary: `Step::backward_checked(&a, 0) == Some(a)`
fn backward_checked(start: Self, count: usize) -> Option<Self>;
/// Returns the value that would be obtained by taking the *predecessor*
/// of `self` `count` times.
///
/// If this would overflow the range of values supported by `Self`,
/// this function is allowed to panic, wrap, or saturate.
/// The suggested behavior is to panic when debug assertions are enabled,
/// and to wrap or saturate otherwise.
///
/// Unsafe code should not rely on the correctness of behavior after overflow.
///
/// # Invariants
///
/// For any `a`, `n`, and `m`, where no overflow occurs:
///
/// * `Step::backward(Step::backward(a, n), m) == Step::backward(a, n + m)`
///
/// For any `a` and `n`, where no overflow occurs:
///
/// * `Step::backward_checked(a, n) == Some(Step::backward(a, n))`
/// * `Step::backward(a, n) == (0..n).fold(a, |x, _| Step::backward(x, 1))`
/// * Corollary: `Step::backward(a, 0) == a`
/// * `Step::backward(a, n) <= a`
/// * `Step::forward(Step::backward(a, n), n) == a`
fn backward(start: Self, count: usize) -> Self {
Step::backward_checked(start, count).expect("overflow in `Step::backward`")
}
/// Returns the value that would be obtained by taking the *predecessor*
/// of `self` `count` times.
///
/// # Safety
///
/// It is undefined behavior for this operation to overflow the
/// range of values supported by `Self`. If you cannot guarantee that this
/// will not overflow, use `backward` or `backward_checked` instead.
///
/// # Invariants
///
/// For any `a`:
///
/// * if there exists `b` such that `b < a`, it is safe to call `Step::backward_unchecked(a, 1)`
/// * if there exists `b`, `n` such that `steps_between(&b, &a) == Some(n)`,
/// it is safe to call `Step::backward_unchecked(a, m)` for any `m <= n`.
///
/// For any `a` and `n`, where no overflow occurs:
///
/// * `Step::backward_unchecked(a, n)` is equivalent to `Step::backward(a, n)`
#[unstable(feature = "unchecked_math", reason = "niche optimization path", issue = "none")]
unsafe fn backward_unchecked(start: Self, count: usize) -> Self {
Step::backward(start, count)
}
}
```
Note that all of these are associated functions and not callable via method syntax; the calling syntax is always `Step::forward(start, n)`. This version of the trait additionally changes the stepping functions to talk their arguments by value.
As opposed to previous attempts which provided a "step by one" method directly, this version of the trait only exposes "step by n". There are a few reasons for this:
- `Range*`, the primary consumer of `Step`, assumes that the "step by n" operation is cheap. If a single step function is provided, it will be a lot more enticing to implement "step by n" as n repeated calls to "step by one". While this is not strictly incorrect, this behavior would be surprising for anyone used to using `Range<{primitive integer}>`.
- With a trivial default impl, this can be easily added backwards-compatibly later.
- The debug-wrapping "step by n" needs to exist for `RangeFrom` to be consistent between "step by n" and "step by one" operation. (Note: the behavior is not changed by this PR, but making the behavior consistent is made tenable by this PR.)
Three "kinds" of step are provided: `_checked`, which returns an `Option` indicating attempted overflow; (unsuffixed), which provides "safe overflow" behavior (is allowed to panic, wrap, or saturate, depending on what is most convenient for a given type); and `_unchecked`, which is a version which assumes overflow does not happen.
Review is appreciated to check that:
- The invariants as described on the `Step` functions are enough to specify the "common sense" consistency for successor/predecessor.
- Implementation of `Step` functions is correct in the face of overflow and the edges of representable integers.
- Added tests of `Step` functions are asserting the correct behavior (and not just the implemented behavior).
|
|
|
|
The previous definition did not optimize down to a single add operation,
but this version does appear to.
|
|
|
|
Currently it uses `for x in self`, which seems dubious within an
iterator method. Furthermore, `self.next()` is used in all the other
iterator methods.
|
|
- Remove a type parameter from `[A]RcFromIter`.
- Remove an implementation of `[A]RcFromIter` that didn't actually
specialize anything.
- Remove unused implementation of `IsZero` for `Option<&mut T>`.
- Change specializations of `[A]RcEqIdent` to use a marker trait version
of `Eq`.
- Remove `BTreeClone`. I couldn't find a way to make this work with
`min_specialization`.
- Add `rustc_unsafe_specialization_marker` to `Copy` and `TrustedLen`.
|
|
|
|
Document unsafety in core::{panicking, alloc::layout, hint, iter::adapters::zip}
Helps with #66219.
r? @Mark-Simulacrum do you want to continue reading safety comments? :D
|
|
iter::adapters::zip}`
|
|
Don't fuse Chain in its second iterator
Only the "first" iterator is actually set `None` when exhausted,
depending on whether you iterate forward or backward. This restores
behavior similar to the former `ChainState`, where it would transition
from `Both` to `Front`/`Back` and only continue from that side.
However, if you mix directions, then this may still set both sides to
`None`, totally fusing the iterator.
Fixes #71375
r? @scottmcm
|
|
Only the "first" iterator is actually set `None` when exhausted,
depending on whether you iterate forward or backward. This restores
behavior similar to the former `ChainState`, where it would transition
from `Both` to `Front`/`Back` and only continue from that side.
However, if you mix directions, then this may still set both sides to
`None`, totally fusing the iterator.
|
|
|
|
Dogfood or_patterns in the standard library
We can start using `or_patterns` in the standard library as a step toward stabilization.
cc #54883 @Centril
|
|
Hides default fns inside Fuse impl to avoid exposing it to any crate
Fixes #70796
@cuviper I've added some default, private traits to do the job for us. If required, I can expose them to a specific visibility if you want to call these functions for #70332
r? @cuviper
|
|
|
|
|
|
Removed unnecessarry empty impls.
Moved code to organise it better
|
|
Co-Authored-By: Nadrieril Feneanar <nadrieril@users.noreply.github.com>
|
|
|
|
FuseExactSizeIteratorImpl to avoid exposing default functions outside of the current crate.
|
|
|
|
|
|
|
|
The iterators are now "fused" with `Option` so we don't need separate
state to track which part is already exhausted, and we may also get
niche layout for `None`. We don't use the real `Fuse` adapter because
its specialization for `FusedIterator` unconditionally descends into the
iterator, and that could be expensive to keep revisiting stuff like
nested chains. It also hurts compiler performance to add more iterator
layers to `Chain`.
|
|
Match options directly in the Fuse implementation
Rather than using `as_ref()`, `as_mut()`, and `?`, we can use `match` directly to save a lot of generated code. This was mentioned as a possibility in https://github.com/rust-lang/rust/pull/70366#issuecomment-603462546, and I found that it had a very large impact on #70332 using `Fuse` within `Chain`. Let's evaluate this change on its own first.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- Added `Iterator::fold_first`, which is like `fold`, but uses the first element in the iterator as the initial accumulator
- Includes doc and doctest
- Rebase commit; see #65222 for details
Co-Authored-By: Tim Vermeulen <tvermeulen@me.com>
|
|
|
|
The former `done` flag was roughly similar to an `Option` tag, but left
the possibity of misuse. By using a real `Option`, we can set `None`
when the iterator is exhausted, removing any way to call it again. We
also allow niche layout this way, so the `Fuse` may be smaller.
The `FusedIterator` specialization does want to ignore the possibility
of exhaustion though, so it uses `unsafe { intrinsics::unreachable() }`
to optimize that branch away. The entire `Fuse` implementation is now
isolated in its own module to contain that unsafety.
|
|
r=LukasKalbertodt
Remove `finished` flag from `MapWhile`
This PR removes `finished` flag from `MapWhile` as been proposed in https://github.com/rust-lang/rust/pull/66577#discussion_r370958025.
This also resolves open questions of the tracking issue (#68537):
- `MapWhile` can't implement both
+ `DoubleEndedIterator` (discussed in https://github.com/rust-lang/rust/pull/66577#discussion_r370947990 and following comments)
+ `FusedIterator` (this pr removes `finished` flag, so `MapWhile` isn't fused anymore)
- Debug output (this pr removes `finished` flag, so there is no question in including it in debug output)
r? @Mark-Simulacrum
|
|
|
|
Fix bugs in Peekable and Flatten when using non-fused iterators
I fixed a couple of bugs with regard to the `Peekable` and `Flatten`/`FlatMap` iterators when the underlying iterator isn't fused. For testing, I also added a `NonFused` iterator wrapper that panics when `next` or `next_back` is called on an iterator that has returned `None` before, which will hopefully make it easier to spot these mistakes in the future.
### Peekable
`Peekable::next_back` was implemented as
```rust
self.iter.next_back().or_else(|| self.peeked.take().and_then(|x| x))
```
which is incorrect because when the `peeked` field is `Some(None)`, then `None` has already been returned from the inner iterator and what it returns from `next_back` can no longer be relied upon. `test_peekable_non_fused` tests this.
### Flatten
When a `FlattenCompat` instance only has a `backiter` remaining (i.e. `self.frontiter` is `None` and `self.iter` is empty), then `next` will call `self.iter.next()` every time, so the `iter` field needs to be fused. I fixed it by giving it the type `Fuse<I>` instead of `I`, I think this is the only way to fix it. `test_flatten_non_fused_outer` tests this.
Furthermore, previously `FlattenCompat::next` did not set `self.frontiter` to `None` after it returned `None`, which is incorrect when the inner iterator type isn't fused. I just delegated it to `try_fold` because that already handles it correctly. `test_flatten_non_fused_inner` tests this.
r? @scottmcm
|
|
|
|
Implement nth, last, and count for iter::Copied
Implement nth, last and count for iter::Copied.
|
|
Remove spotlight
I had a few comments saying that this feature was at best misunderstood or not even used so I decided to organize a poll about on [twitter](https://twitter.com/imperioworld_/status/1232769353503956994). After 87 votes, the result is very clear: it's not useful. Considering the amount of code we have just to run it, I think it's definitely worth it to remove it.
r? @kinnison
cc @ollie27
|
|
use question mark operator in a few places.
|
|
|
|
Improve documentation on iterators length
Attempts to resolve #66491. @the8472 does this help?
r? @steveklabnik
|
|
|