Skip to content

Commit eb846db

Browse files
committed
Override Iterator::advance(_back)_by for array::IntoIter
Because I happened to notice that `nth` is currently getting codegen'd as a loop even for `Copy` types: <https://rust.godbolt.org/z/fPqv7Gvs7>
1 parent 2a9e083 commit eb846db

File tree

2 files changed

+149
-1
lines changed

2 files changed

+149
-1
lines changed

library/core/src/array/iter.rs

+43-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Defines the `IntoIter` owned iterator for arrays.
22
33
use crate::{
4-
fmt,
4+
cmp, fmt,
55
iter::{self, ExactSizeIterator, FusedIterator, TrustedLen},
66
mem::{self, MaybeUninit},
77
ops::Range,
@@ -150,6 +150,27 @@ impl<T, const N: usize> Iterator for IntoIter<T, N> {
150150
fn last(mut self) -> Option<Self::Item> {
151151
self.next_back()
152152
}
153+
154+
fn advance_by(&mut self, n: usize) -> Result<(), usize> {
155+
let len = self.len();
156+
157+
// The number of elements to drop. Always in-bounds by construction.
158+
let delta = cmp::min(n, len);
159+
160+
let range_to_drop = self.alive.start..(self.alive.start + delta);
161+
162+
// Moving the start marks them as conceptually "dropped", so if anything
163+
// goes bad then our drop impl won't double-free them.
164+
self.alive.start += delta;
165+
166+
// SAFETY: These elements are currently initialized, so it's fine to drop them.
167+
unsafe {
168+
let slice = self.data.get_unchecked_mut(range_to_drop);
169+
ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice));
170+
}
171+
172+
if n > len { Err(len) } else { Ok(()) }
173+
}
153174
}
154175

155176
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
@@ -170,6 +191,27 @@ impl<T, const N: usize> DoubleEndedIterator for IntoIter<T, N> {
170191
unsafe { self.data.get_unchecked(idx).assume_init_read() }
171192
})
172193
}
194+
195+
fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
196+
let len = self.len();
197+
198+
// The number of elements to drop. Always in-bounds by construction.
199+
let delta = cmp::min(n, len);
200+
201+
let range_to_drop = (self.alive.end - delta)..self.alive.end;
202+
203+
// Moving the end marks them as conceptually "dropped", so if anything
204+
// goes bad then our drop impl won't double-free them.
205+
self.alive.end -= delta;
206+
207+
// SAFETY: These elements are currently initialized, so it's fine to drop them.
208+
unsafe {
209+
let slice = self.data.get_unchecked_mut(range_to_drop);
210+
ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice));
211+
}
212+
213+
if n > len { Err(len) } else { Ok(()) }
214+
}
173215
}
174216

175217
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]

library/core/tests/array.rs

+106
Original file line numberDiff line numberDiff line change
@@ -474,3 +474,109 @@ fn array_split_array_mut_out_of_bounds() {
474474

475475
v.split_array_mut::<7>();
476476
}
477+
478+
#[test]
479+
fn array_intoiter_advance_by() {
480+
use std::cell::Cell;
481+
struct DropCounter<'a>(usize, &'a Cell<usize>);
482+
impl Drop for DropCounter<'_> {
483+
fn drop(&mut self) {
484+
let x = self.1.get();
485+
self.1.set(x + 1);
486+
}
487+
}
488+
489+
let counter = Cell::new(0);
490+
let a: [_; 100] = std::array::from_fn(|i| DropCounter(i, &counter));
491+
let mut it = IntoIterator::into_iter(a);
492+
493+
let r = it.advance_by(1);
494+
assert_eq!(r, Ok(()));
495+
assert_eq!(it.len(), 99);
496+
assert_eq!(counter.get(), 1);
497+
498+
let r = it.advance_by(0);
499+
assert_eq!(r, Ok(()));
500+
assert_eq!(it.len(), 99);
501+
assert_eq!(counter.get(), 1);
502+
503+
let r = it.advance_by(11);
504+
assert_eq!(r, Ok(()));
505+
assert_eq!(it.len(), 88);
506+
assert_eq!(counter.get(), 12);
507+
508+
let x = it.next();
509+
assert_eq!(x.as_ref().map(|x| x.0), Some(12));
510+
assert_eq!(it.len(), 87);
511+
assert_eq!(counter.get(), 12);
512+
drop(x);
513+
assert_eq!(counter.get(), 13);
514+
515+
let r = it.advance_by(123456);
516+
assert_eq!(r, Err(87));
517+
assert_eq!(it.len(), 0);
518+
assert_eq!(counter.get(), 100);
519+
520+
let r = it.advance_by(0);
521+
assert_eq!(r, Ok(()));
522+
assert_eq!(it.len(), 0);
523+
assert_eq!(counter.get(), 100);
524+
525+
let r = it.advance_by(10);
526+
assert_eq!(r, Err(0));
527+
assert_eq!(it.len(), 0);
528+
assert_eq!(counter.get(), 100);
529+
}
530+
531+
#[test]
532+
fn array_intoiter_advance_back_by() {
533+
use std::cell::Cell;
534+
struct DropCounter<'a>(usize, &'a Cell<usize>);
535+
impl Drop for DropCounter<'_> {
536+
fn drop(&mut self) {
537+
let x = self.1.get();
538+
self.1.set(x + 1);
539+
}
540+
}
541+
542+
let counter = Cell::new(0);
543+
let a: [_; 100] = std::array::from_fn(|i| DropCounter(i, &counter));
544+
let mut it = IntoIterator::into_iter(a);
545+
546+
let r = it.advance_back_by(1);
547+
assert_eq!(r, Ok(()));
548+
assert_eq!(it.len(), 99);
549+
assert_eq!(counter.get(), 1);
550+
551+
let r = it.advance_back_by(0);
552+
assert_eq!(r, Ok(()));
553+
assert_eq!(it.len(), 99);
554+
assert_eq!(counter.get(), 1);
555+
556+
let r = it.advance_back_by(11);
557+
assert_eq!(r, Ok(()));
558+
assert_eq!(it.len(), 88);
559+
assert_eq!(counter.get(), 12);
560+
561+
let x = it.next_back();
562+
assert_eq!(x.as_ref().map(|x| x.0), Some(87));
563+
assert_eq!(it.len(), 87);
564+
assert_eq!(counter.get(), 12);
565+
drop(x);
566+
assert_eq!(counter.get(), 13);
567+
568+
let r = it.advance_back_by(123456);
569+
assert_eq!(r, Err(87));
570+
assert_eq!(it.len(), 0);
571+
assert_eq!(counter.get(), 100);
572+
573+
let r = it.advance_back_by(0);
574+
assert_eq!(r, Ok(()));
575+
assert_eq!(it.len(), 0);
576+
assert_eq!(counter.get(), 100);
577+
578+
let r = it.advance_back_by(10);
579+
assert_eq!(r, Err(0));
580+
assert_eq!(it.len(), 0);
581+
assert_eq!(counter.get(), 100);
582+
}

0 commit comments

Comments
 (0)