Auto merge of #110353 - the8472:in-place-flatten-chunks, r=cuviper

Expand in-place iteration specialization to Flatten, FlatMap and ArrayChunks

This enables the following cases to collect in-place:

```rust
let v = vec![[0u8; 4]; 1024]
let v: Vec<_> = v.into_iter().flatten().collect();

let v: Vec<Option<NonZeroUsize>> = vec![NonZeroUsize::new(0); 1024];
let v: Vec<_> = v.into_iter().flatten().collect();

let v = vec![u8; 4096];
let v: Vec<_> = v.into_iter().array_chunks::<4>().collect();
```

Especially the nicheful-option-flattening should be useful in real code.
This commit is contained in:
bors 2023-11-28 12:22:16 +00:00
commit df0295f071
25 changed files with 488 additions and 71 deletions

View file

@ -145,7 +145,7 @@
use core::alloc::Allocator;
use core::fmt;
use core::iter::{FusedIterator, InPlaceIterable, SourceIter, TrustedLen};
use core::iter::{FusedIterator, InPlaceIterable, SourceIter, TrustedFused, TrustedLen};
use core::mem::{self, swap, ManuallyDrop};
use core::num::NonZeroUsize;
use core::ops::{Deref, DerefMut};
@ -1542,6 +1542,10 @@ impl<T, A: Allocator> ExactSizeIterator for IntoIter<T, A> {
#[stable(feature = "fused", since = "1.26.0")]
impl<T, A: Allocator> FusedIterator for IntoIter<T, A> {}
#[doc(hidden)]
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<T, A: Allocator> TrustedFused for IntoIter<T, A> {}
#[stable(feature = "default_iters", since = "1.70.0")]
impl<T> Default for IntoIter<T> {
/// Creates an empty `binary_heap::IntoIter`.
@ -1571,7 +1575,10 @@ unsafe impl<T, A: Allocator> SourceIter for IntoIter<T, A> {
#[unstable(issue = "none", feature = "inplace_iteration")]
#[doc(hidden)]
unsafe impl<I, A: Allocator> InPlaceIterable for IntoIter<I, A> {}
unsafe impl<I, A: Allocator> InPlaceIterable for IntoIter<I, A> {
const EXPAND_BY: Option<NonZeroUsize> = NonZeroUsize::new(1);
const MERGE_BY: Option<NonZeroUsize> = NonZeroUsize::new(1);
}
unsafe impl<I> AsVecIntoIter for IntoIter<I> {
type Item = I;

View file

@ -154,6 +154,7 @@
#![feature(std_internals)]
#![feature(str_internals)]
#![feature(strict_provenance)]
#![feature(trusted_fused)]
#![feature(trusted_len)]
#![feature(trusted_random_access)]
#![feature(try_trait_v2)]

View file

@ -6,11 +6,11 @@
//! The specialization in this module applies to iterators in the shape of
//! `source.adapter().adapter().adapter().collect::<Vec<U>>()`
//! where `source` is an owning iterator obtained from [`Vec<T>`], [`Box<[T]>`][box] (by conversion to `Vec`)
//! or [`BinaryHeap<T>`], the adapters each consume one or more items per step
//! (represented by [`InPlaceIterable`]), provide transitive access to `source` (via [`SourceIter`])
//! and thus the underlying allocation. And finally the layouts of `T` and `U` must
//! have the same size and alignment, this is currently ensured via const eval instead of trait bounds
//! in the specialized [`SpecFromIter`] implementation.
//! or [`BinaryHeap<T>`], the adapters guarantee to consume enough items per step to make room
//! for the results (represented by [`InPlaceIterable`]), provide transitive access to `source`
//! (via [`SourceIter`]) and thus the underlying allocation.
//! And finally there are alignment and size constriants to consider, this is currently ensured via
//! const eval instead of trait bounds in the specialized [`SpecFromIter`] implementation.
//!
//! [`BinaryHeap<T>`]: crate::collections::BinaryHeap
//! [box]: crate::boxed::Box
@ -35,11 +35,28 @@
//! the step of reading a value and getting a reference to write to. Instead raw pointers must be
//! used on the reader and writer side.
//!
//! That writes never clobber a yet-to-be-read item is ensured by the [`InPlaceIterable`] requirements.
//! That writes never clobber a yet-to-be-read items is ensured by the [`InPlaceIterable`] requirements.
//!
//! # Layout constraints
//!
//! [`Allocator`] requires that `allocate()` and `deallocate()` have matching alignment and size.
//! When recycling an allocation between different types we must uphold the [`Allocator`] contract
//! which means that the input and output Layouts have to "fit".
//!
//! To complicate things further `InPlaceIterable` supports splitting or merging items into smaller/
//! larger ones to enable (de)aggregation of arrays.
//!
//! Ultimately each step of the iterator must free up enough *bytes* in the source to make room
//! for the next output item.
//! If `T` and `U` have the same size no fixup is needed.
//! If `T`'s size is a multiple of `U`'s we can compensate by multiplying the capacity accordingly.
//! Otherwise the input capacity (and thus layout) in bytes may not be representable by the output
//! `Vec<U>`. In that case `alloc.shrink()` is used to update the allocation's layout.
//!
//! Alignments of `T` must be the same or larger than `U`. Since alignments are always a power
//! of two _larger_ implies _is a multiple of_.
//!
//! See `in_place_collectible()` for the current conditions.
//!
//! Additionally this specialization doesn't make sense for ZSTs as there is no reallocation to
//! avoid and it would make pointer arithmetic more difficult.
//!
@ -137,44 +154,73 @@
//! }
//! vec.truncate(write_idx);
//! ```
use crate::alloc::{handle_alloc_error, Global};
use core::alloc::Allocator;
use core::alloc::Layout;
use core::iter::{InPlaceIterable, SourceIter, TrustedRandomAccessNoCoerce};
use core::mem::{self, ManuallyDrop, SizedTypeProperties};
use core::ptr::{self};
use core::num::NonZeroUsize;
use core::ptr::{self, NonNull};
use super::{InPlaceDrop, InPlaceDstBufDrop, SpecFromIter, SpecFromIterNested, Vec};
/// Specialization marker for collecting an iterator pipeline into a Vec while reusing the
/// source allocation, i.e. executing the pipeline in place.
#[rustc_unsafe_specialization_marker]
pub(super) trait InPlaceIterableMarker {}
const fn in_place_collectible<DEST, SRC>(
step_merge: Option<NonZeroUsize>,
step_expand: Option<NonZeroUsize>,
) -> bool {
if DEST::IS_ZST || mem::align_of::<SRC>() < mem::align_of::<DEST>() {
return false;
}
impl<T> InPlaceIterableMarker for T where T: InPlaceIterable {}
match (step_merge, step_expand) {
(Some(step_merge), Some(step_expand)) => {
// At least N merged source items -> at most M expanded destination items
// e.g.
// - 1 x [u8; 4] -> 4x u8, via flatten
// - 4 x u8 -> 1x [u8; 4], via array_chunks
mem::size_of::<SRC>() * step_merge.get() >= mem::size_of::<DEST>() * step_expand.get()
}
// Fall back to other from_iter impls if an overflow occurred in the step merge/expansion
// tracking.
_ => false,
}
}
/// This provides a shorthand for the source type since local type aliases aren't a thing.
#[rustc_specialization_trait]
trait InPlaceCollect: SourceIter<Source: AsVecIntoIter> + InPlaceIterable {
type Src;
}
impl<T> InPlaceCollect for T
where
T: SourceIter<Source: AsVecIntoIter> + InPlaceIterable,
{
type Src = <<T as SourceIter>::Source as AsVecIntoIter>::Item;
}
impl<T, I> SpecFromIter<T, I> for Vec<T>
where
I: Iterator<Item = T> + SourceIter<Source: AsVecIntoIter> + InPlaceIterableMarker,
I: Iterator<Item = T> + InPlaceCollect,
<I as SourceIter>::Source: AsVecIntoIter,
{
default fn from_iter(mut iterator: I) -> Self {
// See "Layout constraints" section in the module documentation. We rely on const
// optimization here since these conditions currently cannot be expressed as trait bounds
if T::IS_ZST
|| mem::size_of::<T>()
!= mem::size_of::<<<I as SourceIter>::Source as AsVecIntoIter>::Item>()
|| mem::align_of::<T>()
!= mem::align_of::<<<I as SourceIter>::Source as AsVecIntoIter>::Item>()
{
if const { !in_place_collectible::<T, I::Src>(I::MERGE_BY, I::EXPAND_BY) } {
// fallback to more generic implementations
return SpecFromIterNested::from_iter(iterator);
}
let (src_buf, src_ptr, dst_buf, dst_end, cap) = unsafe {
let (src_buf, src_ptr, src_cap, mut dst_buf, dst_end, dst_cap) = unsafe {
let inner = iterator.as_inner().as_into_iter();
(
inner.buf.as_ptr(),
inner.ptr,
inner.cap,
inner.buf.as_ptr() as *mut T,
inner.end as *const T,
inner.cap,
inner.cap * mem::size_of::<I::Src>() / mem::size_of::<T>(),
)
};
@ -196,18 +242,55 @@ where
}
// The ownership of the allocation and the new `T` values is temporarily moved into `dst_guard`.
// This is safe because `forget_allocation_drop_remaining` immediately forgets the allocation
// This is safe because
// * `forget_allocation_drop_remaining` immediately forgets the allocation
// before any panic can occur in order to avoid any double free, and then proceeds to drop
// any remaining values at the tail of the source.
// * the shrink either panics without invalidating the allocation, aborts or
// succeeds. In the last case we disarm the guard.
//
// Note: This access to the source wouldn't be allowed by the TrustedRandomIteratorNoCoerce
// contract (used by SpecInPlaceCollect below). But see the "O(1) collect" section in the
// module documentation why this is ok anyway.
let dst_guard = InPlaceDstBufDrop { ptr: dst_buf, len, cap };
let dst_guard = InPlaceDstBufDrop { ptr: dst_buf, len, cap: dst_cap };
src.forget_allocation_drop_remaining();
// Adjust the allocation if the alignment didn't match or the source had a capacity in bytes
// that wasn't a multiple of the destination type size.
// Since the discrepancy should generally be small this should only result in some
// bookkeeping updates and no memmove.
if (const {
let src_sz = mem::size_of::<I::Src>();
src_sz > 0 && mem::size_of::<T>() % src_sz != 0
} && src_cap * mem::size_of::<I::Src>() != dst_cap * mem::size_of::<T>())
|| const { mem::align_of::<T>() != mem::align_of::<I::Src>() }
{
let alloc = Global;
unsafe {
// The old allocation exists, therefore it must have a valid layout.
let src_align = mem::align_of::<I::Src>();
let src_size = mem::size_of::<I::Src>().unchecked_mul(src_cap);
let old_layout = Layout::from_size_align_unchecked(src_size, src_align);
// The must be equal or smaller for in-place iteration to be possible
// therefore the new layout must be ≤ the old one and therefore valid.
let dst_align = mem::align_of::<T>();
let dst_size = mem::size_of::<T>().unchecked_mul(dst_cap);
let new_layout = Layout::from_size_align_unchecked(dst_size, dst_align);
let result = alloc.shrink(
NonNull::new_unchecked(dst_buf as *mut u8),
old_layout,
new_layout,
);
let Ok(reallocated) = result else { handle_alloc_error(new_layout) };
dst_buf = reallocated.as_ptr() as *mut T;
}
}
mem::forget(dst_guard);
let vec = unsafe { Vec::from_raw_parts(dst_buf, len, cap) };
let vec = unsafe { Vec::from_raw_parts(dst_buf, len, dst_cap) };
vec
}

View file

@ -7,7 +7,8 @@ use crate::raw_vec::RawVec;
use core::array;
use core::fmt;
use core::iter::{
FusedIterator, InPlaceIterable, SourceIter, TrustedLen, TrustedRandomAccessNoCoerce,
FusedIterator, InPlaceIterable, SourceIter, TrustedFused, TrustedLen,
TrustedRandomAccessNoCoerce,
};
use core::marker::PhantomData;
use core::mem::{self, ManuallyDrop, MaybeUninit, SizedTypeProperties};
@ -337,6 +338,10 @@ impl<T, A: Allocator> ExactSizeIterator for IntoIter<T, A> {
#[stable(feature = "fused", since = "1.26.0")]
impl<T, A: Allocator> FusedIterator for IntoIter<T, A> {}
#[doc(hidden)]
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<T, A: Allocator> TrustedFused for IntoIter<T, A> {}
#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<T, A: Allocator> TrustedLen for IntoIter<T, A> {}
@ -421,7 +426,10 @@ unsafe impl<#[may_dangle] T, A: Allocator> Drop for IntoIter<T, A> {
// also refer to the vec::in_place_collect module documentation to get an overview
#[unstable(issue = "none", feature = "inplace_iteration")]
#[doc(hidden)]
unsafe impl<T, A: Allocator> InPlaceIterable for IntoIter<T, A> {}
unsafe impl<T, A: Allocator> InPlaceIterable for IntoIter<T, A> {
const EXPAND_BY: Option<NonZeroUsize> = NonZeroUsize::new(1);
const MERGE_BY: Option<NonZeroUsize> = NonZeroUsize::new(1);
}
#[unstable(issue = "none", feature = "inplace_iteration")]
#[doc(hidden)]

View file

@ -1,5 +1,6 @@
#![feature(allocator_api)]
#![feature(alloc_layout_extra)]
#![feature(iter_array_chunks)]
#![feature(assert_matches)]
#![feature(btree_extract_if)]
#![feature(cow_is_borrowed)]

View file

@ -1,6 +1,7 @@
use alloc::vec::Vec;
use core::alloc::{Allocator, Layout};
use core::assert_eq;
use core::iter::IntoIterator;
use core::{assert_eq, assert_ne};
use core::iter::{IntoIterator, Iterator};
use core::num::NonZeroUsize;
use core::ptr::NonNull;
use std::alloc::System;
@ -1184,6 +1185,46 @@ fn test_from_iter_specialization_with_iterator_adapters() {
assert_eq!(srcptr, sinkptr as *const usize);
}
#[test]
fn test_in_place_specialization_step_up_down() {
fn assert_in_place_trait<T: InPlaceIterable>(_: &T) {}
let src = vec![[0u8; 4]; 256];
let srcptr = src.as_ptr();
let src_cap = src.capacity();
let iter = src.into_iter().flatten();
assert_in_place_trait(&iter);
let sink = iter.collect::<Vec<_>>();
let sinkptr = sink.as_ptr();
assert_eq!(srcptr as *const u8, sinkptr);
assert_eq!(src_cap * 4, sink.capacity());
let iter = sink.into_iter().array_chunks::<4>();
assert_in_place_trait(&iter);
let sink = iter.collect::<Vec<_>>();
let sinkptr = sink.as_ptr();
assert_eq!(srcptr, sinkptr);
assert_eq!(src_cap, sink.capacity());
let mut src: Vec<u8> = Vec::with_capacity(17);
let src_bytes = src.capacity();
src.resize(8, 0u8);
let sink: Vec<[u8; 4]> = src.into_iter().array_chunks::<4>().collect();
let sink_bytes = sink.capacity() * 4;
assert_ne!(src_bytes, sink_bytes);
assert_eq!(sink.len(), 2);
let src = vec![[0u8; 4]; 256];
let srcptr = src.as_ptr();
let iter = src
.into_iter()
.flat_map(|a| {
a.into_iter().map(|b| b.wrapping_add(1))
});
assert_in_place_trait(&iter);
let sink = iter.collect::<Vec<_>>();
assert_eq!(srcptr as *const u8, sink.as_ptr());
}
#[test]
fn test_from_iter_specialization_head_tail_drop() {
let drop_count: Vec<_> = (0..=2).map(|_| Rc::new(())).collect();

View file

@ -1,5 +1,9 @@
use crate::array;
use crate::iter::{ByRefSized, FusedIterator, Iterator, TrustedRandomAccessNoCoerce};
use crate::iter::adapters::SourceIter;
use crate::iter::{
ByRefSized, FusedIterator, InPlaceIterable, Iterator, TrustedFused, TrustedRandomAccessNoCoerce,
};
use crate::num::NonZeroUsize;
use crate::ops::{ControlFlow, NeverShortCircuit, Try};
/// An iterator over `N` elements of the iterator at a time.
@ -159,6 +163,9 @@ where
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
impl<I, const N: usize> FusedIterator for ArrayChunks<I, N> where I: FusedIterator {}
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<I, const N: usize> TrustedFused for ArrayChunks<I, N> where I: TrustedFused + Iterator {}
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
impl<I, const N: usize> ExactSizeIterator for ArrayChunks<I, N>
where
@ -229,3 +236,28 @@ where
accum
}
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I, const N: usize> SourceIter for ArrayChunks<I, N>
where
I: SourceIter + Iterator,
{
type Source = I::Source;
#[inline]
unsafe fn as_inner(&mut self) -> &mut I::Source {
// SAFETY: unsafe function forwarding to unsafe function with the same requirements
unsafe { SourceIter::as_inner(&mut self.iter) }
}
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: InPlaceIterable + Iterator, const N: usize> InPlaceIterable for ArrayChunks<I, N> {
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = const {
match (I::MERGE_BY, NonZeroUsize::new(N)) {
(Some(m), Some(n)) => m.checked_mul(n),
_ => None,
}
};
}

View file

@ -1,7 +1,7 @@
use crate::iter::adapters::{
zip::try_get_unchecked, SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce,
};
use crate::iter::{FusedIterator, InPlaceIterable, TrustedLen};
use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused, TrustedLen};
use crate::num::NonZeroUsize;
use crate::ops::Try;
@ -243,6 +243,9 @@ where
#[stable(feature = "fused", since = "1.26.0")]
impl<I> FusedIterator for Enumerate<I> where I: FusedIterator {}
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<I: TrustedFused> TrustedFused for Enumerate<I> {}
#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<I> TrustedLen for Enumerate<I> where I: TrustedLen {}
@ -261,7 +264,10 @@ where
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: InPlaceIterable> InPlaceIterable for Enumerate<I> {}
unsafe impl<I: InPlaceIterable> InPlaceIterable for Enumerate<I> {
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}
#[stable(feature = "default_iters", since = "1.70.0")]
impl<I: Default> Default for Enumerate<I> {

View file

@ -1,5 +1,6 @@
use crate::fmt;
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable};
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused};
use crate::num::NonZeroUsize;
use crate::ops::Try;
use core::array;
use core::mem::{ManuallyDrop, MaybeUninit};
@ -189,6 +190,9 @@ where
#[stable(feature = "fused", since = "1.26.0")]
impl<I: FusedIterator, P> FusedIterator for Filter<I, P> where P: FnMut(&I::Item) -> bool {}
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<I: TrustedFused, F> TrustedFused for Filter<I, F> {}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<P, I> SourceIter for Filter<I, P>
where
@ -204,4 +208,7 @@ where
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: InPlaceIterable, P> InPlaceIterable for Filter<I, P> where P: FnMut(&I::Item) -> bool {}
unsafe impl<I: InPlaceIterable, P> InPlaceIterable for Filter<I, P> {
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}

View file

@ -1,5 +1,6 @@
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable};
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused};
use crate::mem::{ManuallyDrop, MaybeUninit};
use crate::num::NonZeroUsize;
use crate::ops::{ControlFlow, Try};
use crate::{array, fmt};
@ -190,6 +191,9 @@ where
#[stable(feature = "fused", since = "1.26.0")]
impl<B, I: FusedIterator, F> FusedIterator for FilterMap<I, F> where F: FnMut(I::Item) -> Option<B> {}
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<I: TrustedFused, F> TrustedFused for FilterMap<I, F> {}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I, F> SourceIter for FilterMap<I, F>
where
@ -205,7 +209,7 @@ where
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<B, I: InPlaceIterable, F> InPlaceIterable for FilterMap<I, F> where
F: FnMut(I::Item) -> Option<B>
{
unsafe impl<I: InPlaceIterable, F> InPlaceIterable for FilterMap<I, F> {
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}

View file

@ -1,7 +1,13 @@
use crate::fmt;
use crate::iter::{DoubleEndedIterator, Fuse, FusedIterator, Iterator, Map, TrustedLen};
use crate::iter::adapters::SourceIter;
use crate::iter::{
Cloned, Copied, DoubleEndedIterator, Filter, FilterMap, Fuse, FusedIterator, InPlaceIterable,
Iterator, Map, TrustedFused, TrustedLen,
};
use crate::iter::{Once, OnceWith};
use crate::num::NonZeroUsize;
use crate::ops::{ControlFlow, Try};
use crate::result;
use crate::{array, fmt, option};
/// An iterator that maps each element to an iterator, and yields the elements
/// of the produced iterators.
@ -145,6 +151,91 @@ where
{
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I, U, F> InPlaceIterable for FlatMap<I, U, F>
where
I: InPlaceIterable,
U: BoundedSize + IntoIterator,
{
const EXPAND_BY: Option<NonZeroUsize> = const {
match (I::EXPAND_BY, U::UPPER_BOUND) {
(Some(m), Some(n)) => m.checked_mul(n),
_ => None,
}
};
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I, U, F> SourceIter for FlatMap<I, U, F>
where
I: SourceIter + TrustedFused,
U: IntoIterator,
{
type Source = I::Source;
#[inline]
unsafe fn as_inner(&mut self) -> &mut I::Source {
// SAFETY: unsafe function forwarding to unsafe function with the same requirements
unsafe { SourceIter::as_inner(&mut self.inner.iter) }
}
}
/// Marker trait for iterators/iterables which have a statically known upper
/// bound of the number of items they can produce.
///
/// # Safety
///
/// Implementations must not yield more elements than indicated by UPPER_BOUND if it is `Some`.
/// Used in specializations. Implementations must not be conditional on lifetimes or
/// user-implementable traits.
#[rustc_specialization_trait]
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe trait BoundedSize {
const UPPER_BOUND: Option<NonZeroUsize> = NonZeroUsize::new(1);
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<T> BoundedSize for Option<T> {}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<T> BoundedSize for option::IntoIter<T> {}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<T, U> BoundedSize for Result<T, U> {}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<T> BoundedSize for result::IntoIter<T> {}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<T> BoundedSize for Once<T> {}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<T> BoundedSize for OnceWith<T> {}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<T, const N: usize> BoundedSize for [T; N] {
const UPPER_BOUND: Option<NonZeroUsize> = NonZeroUsize::new(N);
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<T, const N: usize> BoundedSize for array::IntoIter<T, N> {
const UPPER_BOUND: Option<NonZeroUsize> = NonZeroUsize::new(N);
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: BoundedSize, P> BoundedSize for Filter<I, P> {
const UPPER_BOUND: Option<NonZeroUsize> = I::UPPER_BOUND;
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: BoundedSize, P> BoundedSize for FilterMap<I, P> {
const UPPER_BOUND: Option<NonZeroUsize> = I::UPPER_BOUND;
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: BoundedSize, F> BoundedSize for Map<I, F> {
const UPPER_BOUND: Option<NonZeroUsize> = I::UPPER_BOUND;
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: BoundedSize> BoundedSize for Copied<I> {
const UPPER_BOUND: Option<NonZeroUsize> = I::UPPER_BOUND;
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: BoundedSize> BoundedSize for Cloned<I> {
const UPPER_BOUND: Option<NonZeroUsize> = I::UPPER_BOUND;
}
/// An iterator that flattens one level of nesting in an iterator of things
/// that can be turned into iterators.
///
@ -289,6 +380,36 @@ where
{
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I> InPlaceIterable for Flatten<I>
where
I: InPlaceIterable + Iterator,
<I as Iterator>::Item: IntoIterator + BoundedSize,
{
const EXPAND_BY: Option<NonZeroUsize> = const {
match (I::EXPAND_BY, I::Item::UPPER_BOUND) {
(Some(m), Some(n)) => m.checked_mul(n),
_ => None,
}
};
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I> SourceIter for Flatten<I>
where
I: SourceIter + TrustedFused + Iterator,
<I as Iterator>::Item: IntoIterator,
{
type Source = I::Source;
#[inline]
unsafe fn as_inner(&mut self) -> &mut I::Source {
// SAFETY: unsafe function forwarding to unsafe function with the same requirements
unsafe { SourceIter::as_inner(&mut self.inner.iter) }
}
}
#[stable(feature = "default_iters", since = "1.70.0")]
impl<I> Default for Flatten<I>
where

View file

@ -1,8 +1,9 @@
use crate::intrinsics;
use crate::iter::adapters::zip::try_get_unchecked;
use crate::iter::adapters::SourceIter;
use crate::iter::{
DoubleEndedIterator, ExactSizeIterator, FusedIterator, TrustedLen, TrustedRandomAccess,
TrustedRandomAccessNoCoerce,
DoubleEndedIterator, ExactSizeIterator, FusedIterator, TrustedFused, TrustedLen,
TrustedRandomAccess, TrustedRandomAccessNoCoerce,
};
use crate::ops::Try;
@ -29,6 +30,9 @@ impl<I> Fuse<I> {
#[stable(feature = "fused", since = "1.26.0")]
impl<I> FusedIterator for Fuse<I> where I: Iterator {}
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<I> TrustedFused for Fuse<I> where I: TrustedFused {}
// Any specialized implementation here is made internal
// to avoid exposing default fns outside this trait.
#[stable(feature = "rust1", since = "1.0.0")]
@ -418,6 +422,23 @@ where
}
}
// This is used by Flatten's SourceIter impl
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I> SourceIter for Fuse<I>
where
I: SourceIter + TrustedFused,
{
type Source = I::Source;
#[inline]
unsafe fn as_inner(&mut self) -> &mut I::Source {
// SAFETY: unsafe function forwarding to unsafe function with the same requirements.
// TrustedFused guarantees that we'll never encounter a case where `self.iter` would
// be set to None.
unsafe { SourceIter::as_inner(self.iter.as_mut().unwrap_unchecked()) }
}
}
#[inline]
fn and_then_or_clear<T, U>(opt: &mut Option<T>, f: impl FnOnce(&mut T) -> Option<U>) -> Option<U> {
let x = f(opt.as_mut()?);

View file

@ -1,5 +1,6 @@
use crate::fmt;
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable};
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused};
use crate::num::NonZeroUsize;
use crate::ops::Try;
/// An iterator that calls a function with a reference to each element before
@ -148,6 +149,9 @@ where
#[stable(feature = "fused", since = "1.26.0")]
impl<I: FusedIterator, F> FusedIterator for Inspect<I, F> where F: FnMut(&I::Item) {}
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<I: TrustedFused, F> TrustedFused for Inspect<I, F> {}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I, F> SourceIter for Inspect<I, F>
where
@ -163,4 +167,7 @@ where
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: InPlaceIterable, F> InPlaceIterable for Inspect<I, F> where F: FnMut(&I::Item) {}
unsafe impl<I: InPlaceIterable, F> InPlaceIterable for Inspect<I, F> {
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}

View file

@ -2,7 +2,8 @@ use crate::fmt;
use crate::iter::adapters::{
zip::try_get_unchecked, SourceIter, TrustedRandomAccess, TrustedRandomAccessNoCoerce,
};
use crate::iter::{FusedIterator, InPlaceIterable, TrustedLen, UncheckedIterator};
use crate::iter::{FusedIterator, InPlaceIterable, TrustedFused, TrustedLen, UncheckedIterator};
use crate::num::NonZeroUsize;
use crate::ops::Try;
/// An iterator that maps the values of `iter` with `f`.
@ -179,6 +180,9 @@ where
#[stable(feature = "fused", since = "1.26.0")]
impl<B, I: FusedIterator, F> FusedIterator for Map<I, F> where F: FnMut(I::Item) -> B {}
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<I: TrustedFused, F> TrustedFused for Map<I, F> {}
#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<B, I, F> TrustedLen for Map<I, F>
where
@ -228,4 +232,7 @@ where
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<B, I: InPlaceIterable, F> InPlaceIterable for Map<I, F> where F: FnMut(I::Item) -> B {}
unsafe impl<I: InPlaceIterable, F> InPlaceIterable for Map<I, F> {
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}

View file

@ -1,5 +1,6 @@
use crate::fmt;
use crate::iter::{adapters::SourceIter, InPlaceIterable};
use crate::num::NonZeroUsize;
use crate::ops::{ControlFlow, Try};
/// An iterator that only accepts elements while `predicate` returns `Some(_)`.
@ -82,7 +83,7 @@ where
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<B, I: InPlaceIterable, P> InPlaceIterable for MapWhile<I, P> where
P: FnMut(I::Item) -> Option<B>
{
unsafe impl<I: InPlaceIterable, P> InPlaceIterable for MapWhile<I, P> {
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}

View file

@ -1,4 +1,5 @@
use crate::iter::{InPlaceIterable, Iterator};
use crate::num::NonZeroUsize;
use crate::ops::{ChangeOutputType, ControlFlow, FromResidual, Residual, Try};
mod array_chunks;
@ -119,8 +120,9 @@ pub unsafe trait SourceIter {
///
/// # Safety
///
/// Implementations of must return the same mutable reference for their lifetime, unless
/// Implementations must return the same mutable reference for their lifetime, unless
/// replaced by a caller.
///
/// Callers may only replace the reference when they stopped iteration and drop the
/// iterator pipeline after extracting the source.
///
@ -228,7 +230,10 @@ where
// in order to return `Some(_)`. Since `iter` has type `I: InPlaceIterable` it's
// guaranteed that at least one item will be moved out from the underlying source.
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I, T, R> InPlaceIterable for GenericShunt<'_, I, R> where
I: Iterator<Item: Try<Output = T, Residual = R>> + InPlaceIterable
unsafe impl<I, R> InPlaceIterable for GenericShunt<'_, I, R>
where
I: InPlaceIterable,
{
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}

View file

@ -1,5 +1,6 @@
use crate::fmt;
use crate::iter::{adapters::SourceIter, InPlaceIterable};
use crate::num::NonZeroUsize;
use crate::ops::{ControlFlow, Try};
/// An iterator to maintain state while iterating another iterator.
@ -92,7 +93,7 @@ where
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<St, F, B, I: InPlaceIterable> InPlaceIterable for Scan<I, St, F> where
F: FnMut(&mut St, I::Item) -> Option<B>
{
unsafe impl<St, F, I: InPlaceIterable> InPlaceIterable for Scan<I, St, F> {
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}

View file

@ -1,4 +1,5 @@
use crate::intrinsics::unlikely;
use crate::iter::TrustedFused;
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable};
use crate::num::NonZeroUsize;
use crate::ops::{ControlFlow, Try};
@ -214,6 +215,9 @@ where
#[stable(feature = "fused", since = "1.26.0")]
impl<I> FusedIterator for Skip<I> where I: FusedIterator {}
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<I: TrustedFused> TrustedFused for Skip<I> {}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I> SourceIter for Skip<I>
where
@ -229,4 +233,7 @@ where
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: InPlaceIterable> InPlaceIterable for Skip<I> {}
unsafe impl<I: InPlaceIterable> InPlaceIterable for Skip<I> {
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}

View file

@ -1,5 +1,6 @@
use crate::fmt;
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable};
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused};
use crate::num::NonZeroUsize;
use crate::ops::Try;
/// An iterator that rejects elements while `predicate` returns `true`.
@ -104,6 +105,9 @@ where
{
}
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<I: TrustedFused, P> TrustedFused for SkipWhile<I, P> {}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<P, I> SourceIter for SkipWhile<I, P>
where
@ -119,7 +123,7 @@ where
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: InPlaceIterable, F> InPlaceIterable for SkipWhile<I, F> where
F: FnMut(&I::Item) -> bool
{
unsafe impl<I: InPlaceIterable, F> InPlaceIterable for SkipWhile<I, F> {
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}

View file

@ -1,6 +1,7 @@
use crate::cmp;
use crate::iter::{
adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedLen, TrustedRandomAccess,
adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused, TrustedLen,
TrustedRandomAccess,
};
use crate::num::NonZeroUsize;
use crate::ops::{ControlFlow, Try};
@ -143,7 +144,10 @@ where
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: InPlaceIterable> InPlaceIterable for Take<I> {}
unsafe impl<I: InPlaceIterable> InPlaceIterable for Take<I> {
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}
#[stable(feature = "double_ended_take_iterator", since = "1.38.0")]
impl<I> DoubleEndedIterator for Take<I>
@ -241,6 +245,9 @@ impl<I> ExactSizeIterator for Take<I> where I: ExactSizeIterator {}
#[stable(feature = "fused", since = "1.26.0")]
impl<I> FusedIterator for Take<I> where I: FusedIterator {}
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<I: TrustedFused> TrustedFused for Take<I> {}
#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<I: TrustedLen> TrustedLen for Take<I> {}

View file

@ -1,5 +1,6 @@
use crate::fmt;
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable};
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedFused};
use crate::num::NonZeroUsize;
use crate::ops::{ControlFlow, Try};
/// An iterator that only accepts elements while `predicate` returns `true`.
@ -105,6 +106,9 @@ where
{
}
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<I: TrustedFused, P> TrustedFused for TakeWhile<I, P> {}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<P, I> SourceIter for TakeWhile<I, P>
where
@ -120,7 +124,7 @@ where
}
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<I: InPlaceIterable, F> InPlaceIterable for TakeWhile<I, F> where
F: FnMut(&I::Item) -> bool
{
unsafe impl<I: InPlaceIterable, F> InPlaceIterable for TakeWhile<I, F> {
const EXPAND_BY: Option<NonZeroUsize> = I::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = I::MERGE_BY;
}

View file

@ -1,7 +1,8 @@
use crate::cmp;
use crate::fmt::{self, Debug};
use crate::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator};
use crate::iter::{DoubleEndedIterator, ExactSizeIterator, FusedIterator, Iterator, TrustedFused};
use crate::iter::{InPlaceIterable, SourceIter, TrustedLen, UncheckedIterator};
use crate::num::NonZeroUsize;
/// An iterator that iterates two other iterators simultaneously.
///
@ -446,6 +447,14 @@ where
{
}
#[unstable(issue = "none", feature = "trusted_fused")]
unsafe impl<A, B> TrustedFused for Zip<A, B>
where
A: TrustedFused,
B: TrustedFused,
{
}
#[unstable(feature = "trusted_len", issue = "37572")]
unsafe impl<A, B> TrustedLen for Zip<A, B>
where
@ -479,7 +488,10 @@ where
// Since SourceIter forwards the left hand side we do the same here
#[unstable(issue = "none", feature = "inplace_iteration")]
unsafe impl<A: InPlaceIterable, B: Iterator> InPlaceIterable for Zip<A, B> {}
unsafe impl<A: InPlaceIterable, B> InPlaceIterable for Zip<A, B> {
const EXPAND_BY: Option<NonZeroUsize> = A::EXPAND_BY;
const MERGE_BY: Option<NonZeroUsize> = A::MERGE_BY;
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<A: Debug, B: Debug> Debug for Zip<A, B> {

View file

@ -417,6 +417,8 @@ pub use self::sources::{successors, Successors};
pub use self::traits::FusedIterator;
#[unstable(issue = "none", feature = "inplace_iteration")]
pub use self::traits::InPlaceIterable;
#[unstable(issue = "none", feature = "trusted_fused")]
pub use self::traits::TrustedFused;
#[unstable(feature = "trusted_len", issue = "37572")]
pub use self::traits::TrustedLen;
#[unstable(feature = "trusted_step", issue = "85731")]

View file

@ -1,4 +1,16 @@
use crate::iter::Step;
use crate::num::NonZeroUsize;
/// Same as FusedIterator
///
/// # Safety
///
/// This is used for specialization. Therefore implementations must not
/// be lifetime-dependent.
#[unstable(issue = "none", feature = "trusted_fused")]
#[doc(hidden)]
#[rustc_specialization_trait]
pub unsafe trait TrustedFused {}
/// An iterator that always continues to yield `None` when exhausted.
///
@ -14,6 +26,8 @@ use crate::iter::Step;
/// [`Fuse`]: crate::iter::Fuse
#[stable(feature = "fused", since = "1.26.0")]
#[rustc_unsafe_specialization_marker]
// FIXME: this should be a #[marker] and have another blanket impl for T: TrustedFused
// but that ICEs iter::Fuse specializations.
pub trait FusedIterator: Iterator {}
#[stable(feature = "fused", since = "1.26.0")]
@ -71,7 +85,19 @@ unsafe impl<I: TrustedLen + ?Sized> TrustedLen for &mut I {}
/// [`try_fold()`]: Iterator::try_fold
#[unstable(issue = "none", feature = "inplace_iteration")]
#[doc(hidden)]
pub unsafe trait InPlaceIterable: Iterator {}
#[rustc_specialization_trait]
pub unsafe trait InPlaceIterable {
/// The product of one-to-many item expansions that happen throughout the iterator pipeline.
/// E.g. [[u8; 4]; 4].iter().flatten().flatten() would have a `EXPAND_BY` of 16.
/// This is an upper bound, i.e. the transformations will produce at most this many items per
/// input. It's meant for layout calculations.
const EXPAND_BY: Option<NonZeroUsize>;
/// The product of many-to-one item reductions that happen throughout the iterator pipeline.
/// E.g. [u8].iter().array_chunks::<4>().array_chunks::<4>() would have a `MERGE_BY` of 16.
/// This is a lower bound, i.e. the transformations will consume at least this many items per
/// output.
const MERGE_BY: Option<NonZeroUsize>;
}
/// A type that upholds all invariants of [`Step`].
///

View file

@ -18,6 +18,8 @@ pub use self::{
#[unstable(issue = "none", feature = "inplace_iteration")]
pub use self::marker::InPlaceIterable;
#[unstable(issue = "none", feature = "trusted_fused")]
pub use self::marker::TrustedFused;
#[unstable(feature = "trusted_step", issue = "85731")]
pub use self::marker::TrustedStep;