Skip to content

Commit fabf502

Browse files
committed
Auto merge of #87168 - the8472:flatten-len, r=scottmcm
implement TrustedLen for Flatten/FlatMap if the U: IntoIterator == [T; N] This only works if arrays are passed directly instead of array iterators because we need to be sure that they have not been advanced before Flatten does its size calculation. resolves #87094
2 parents b41936b + c3ac8d8 commit fabf502

File tree

2 files changed

+133
-1
lines changed

2 files changed

+133
-1
lines changed

library/core/src/iter/adapters/flatten.rs

+93-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::fmt;
2-
use crate::iter::{DoubleEndedIterator, Fuse, FusedIterator, Iterator, Map};
2+
use crate::iter::{DoubleEndedIterator, Fuse, FusedIterator, Iterator, Map, TrustedLen};
33
use crate::ops::Try;
44

55
/// An iterator that maps each element to an iterator, and yields the elements
@@ -114,6 +114,30 @@ where
114114
{
115115
}
116116

117+
#[unstable(feature = "trusted_len", issue = "37572")]
118+
unsafe impl<T, I, F, const N: usize> TrustedLen for FlatMap<I, [T; N], F>
119+
where
120+
I: TrustedLen,
121+
F: FnMut(I::Item) -> [T; N],
122+
{
123+
}
124+
125+
#[unstable(feature = "trusted_len", issue = "37572")]
126+
unsafe impl<'a, T, I, F, const N: usize> TrustedLen for FlatMap<I, &'a [T; N], F>
127+
where
128+
I: TrustedLen,
129+
F: FnMut(I::Item) -> &'a [T; N],
130+
{
131+
}
132+
133+
#[unstable(feature = "trusted_len", issue = "37572")]
134+
unsafe impl<'a, T, I, F, const N: usize> TrustedLen for FlatMap<I, &'a mut [T; N], F>
135+
where
136+
I: TrustedLen,
137+
F: FnMut(I::Item) -> &'a mut [T; N],
138+
{
139+
}
140+
117141
/// An iterator that flattens one level of nesting in an iterator of things
118142
/// that can be turned into iterators.
119143
///
@@ -230,6 +254,14 @@ where
230254
{
231255
}
232256

257+
#[unstable(feature = "trusted_len", issue = "37572")]
258+
unsafe impl<I> TrustedLen for Flatten<I>
259+
where
260+
I: TrustedLen,
261+
<I as Iterator>::Item: TrustedConstSize,
262+
{
263+
}
264+
233265
/// Real logic of both `Flatten` and `FlatMap` which simply delegate to
234266
/// this type.
235267
#[derive(Clone, Debug)]
@@ -282,6 +314,17 @@ where
282314
let (flo, fhi) = self.frontiter.as_ref().map_or((0, Some(0)), U::size_hint);
283315
let (blo, bhi) = self.backiter.as_ref().map_or((0, Some(0)), U::size_hint);
284316
let lo = flo.saturating_add(blo);
317+
318+
if let Some(fixed_size) = <<I as Iterator>::Item as ConstSizeIntoIterator>::size() {
319+
let (lower, upper) = self.iter.size_hint();
320+
321+
let lower = lower.saturating_mul(fixed_size).saturating_add(lo);
322+
let upper =
323+
try { fhi?.checked_add(bhi?)?.checked_add(fixed_size.checked_mul(upper?)?)? };
324+
325+
return (lower, upper);
326+
}
327+
285328
match (self.iter.size_hint(), fhi, bhi) {
286329
((0, Some(0)), Some(a), Some(b)) => (lo, a.checked_add(b)),
287330
_ => (lo, None),
@@ -444,3 +487,52 @@ where
444487
init
445488
}
446489
}
490+
491+
trait ConstSizeIntoIterator: IntoIterator {
492+
// FIXME(#31844): convert to an associated const once specialization supports that
493+
fn size() -> Option<usize>;
494+
}
495+
496+
impl<T> ConstSizeIntoIterator for T
497+
where
498+
T: IntoIterator,
499+
{
500+
#[inline]
501+
default fn size() -> Option<usize> {
502+
None
503+
}
504+
}
505+
506+
impl<T, const N: usize> ConstSizeIntoIterator for [T; N] {
507+
#[inline]
508+
fn size() -> Option<usize> {
509+
Some(N)
510+
}
511+
}
512+
513+
impl<T, const N: usize> ConstSizeIntoIterator for &[T; N] {
514+
#[inline]
515+
fn size() -> Option<usize> {
516+
Some(N)
517+
}
518+
}
519+
520+
impl<T, const N: usize> ConstSizeIntoIterator for &mut [T; N] {
521+
#[inline]
522+
fn size() -> Option<usize> {
523+
Some(N)
524+
}
525+
}
526+
527+
#[doc(hidden)]
528+
#[unstable(feature = "std_internals", issue = "none")]
529+
// FIXME(#20400): Instead of this helper trait there should be multiple impl TrustedLen for Flatten<>
530+
// blocks with different bounds on Iterator::Item but the compiler erroneously considers them overlapping
531+
pub unsafe trait TrustedConstSize: IntoIterator {}
532+
533+
#[unstable(feature = "std_internals", issue = "none")]
534+
unsafe impl<T, const N: usize> TrustedConstSize for [T; N] {}
535+
#[unstable(feature = "std_internals", issue = "none")]
536+
unsafe impl<T, const N: usize> TrustedConstSize for &'_ [T; N] {}
537+
#[unstable(feature = "std_internals", issue = "none")]
538+
unsafe impl<T, const N: usize> TrustedConstSize for &'_ mut [T; N] {}

library/core/tests/iter/adapters/flatten.rs

+40
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::*;
2+
use core::array;
23
use core::iter::*;
34

45
#[test]
@@ -109,3 +110,42 @@ fn test_double_ended_flatten() {
109110
assert_eq!(it.next(), None);
110111
assert_eq!(it.next_back(), None);
111112
}
113+
114+
#[test]
115+
fn test_trusted_len_flatten() {
116+
fn assert_trusted_len<T: TrustedLen>(_: &T) {}
117+
let mut iter = array::IntoIter::new([[0; 3]; 4]).flatten();
118+
assert_trusted_len(&iter);
119+
120+
assert_eq!(iter.size_hint(), (12, Some(12)));
121+
iter.next();
122+
assert_eq!(iter.size_hint(), (11, Some(11)));
123+
iter.next_back();
124+
assert_eq!(iter.size_hint(), (10, Some(10)));
125+
126+
let iter = array::IntoIter::new([[(); usize::MAX]; 1]).flatten();
127+
assert_eq!(iter.size_hint(), (usize::MAX, Some(usize::MAX)));
128+
129+
let iter = array::IntoIter::new([[(); usize::MAX]; 2]).flatten();
130+
assert_eq!(iter.size_hint(), (usize::MAX, None));
131+
132+
let mut a = [(); 10];
133+
let mut b = [(); 10];
134+
135+
let iter = array::IntoIter::new([&mut a, &mut b]).flatten();
136+
assert_trusted_len(&iter);
137+
assert_eq!(iter.size_hint(), (20, Some(20)));
138+
core::mem::drop(iter);
139+
140+
let iter = array::IntoIter::new([&a, &b]).flatten();
141+
assert_trusted_len(&iter);
142+
assert_eq!(iter.size_hint(), (20, Some(20)));
143+
144+
let iter = [(), (), ()].iter().flat_map(|_| [(); 1000]);
145+
assert_trusted_len(&iter);
146+
assert_eq!(iter.size_hint(), (3000, Some(3000)));
147+
148+
let iter = [(), ()].iter().flat_map(|_| &a);
149+
assert_trusted_len(&iter);
150+
assert_eq!(iter.size_hint(), (20, Some(20)));
151+
}

0 commit comments

Comments
 (0)