@@ -154,16 +154,39 @@ macro_rules! iterator {
154154
155155 #[ inline]
156156 fn next( & mut self ) -> Option <$elem> {
157- // could be implemented with slices, but this avoids bounds checks
157+ // intentionally not using the helpers because this is
158+ // one of the most mono'd things in the library.
158159
159- // SAFETY: The call to `next_unchecked` is
160- // safe since we check if the iterator is empty first.
160+ let ptr = self . ptr;
161+ let end_or_len = self . end_or_len;
162+ // SAFETY: See inner comments. (For some reason having multiple
163+ // block breaks inlining this -- if you can fix that please do!)
161164 unsafe {
162- if is_empty!( self ) {
163- None
165+ if T :: IS_ZST {
166+ let len = end_or_len. addr( ) ;
167+ if len == 0 {
168+ return None ;
169+ }
170+ // SAFETY: just checked that it's not zero, so subtracting one
171+ // cannot wrap. (Ideally this would be `checked_sub`, which
172+ // does the same thing internally, but as of 2025-02 that
173+ // doesn't optimize quite as small in MIR.)
174+ self . end_or_len = without_provenance_mut( len. unchecked_sub( 1 ) ) ;
164175 } else {
165- Some ( self . next_unchecked( ) )
176+ // SAFETY: by type invariant, the `end_or_len` field is always
177+ // non-null for a non-ZST pointee. (This transmute ensures we
178+ // get `!nonnull` metadata on the load of the field.)
179+ if ptr == crate :: intrinsics:: transmute:: <$ptr, NonNull <T >>( end_or_len) {
180+ return None ;
181+ }
182+ // SAFETY: since it's not empty, per the check above, moving
183+ // forward one keeps us inside the slice, and this is valid.
184+ self . ptr = ptr. add( 1 ) ;
166185 }
186+ // SAFETY: Now that we know it wasn't empty and we've moved past
187+ // the first one (to avoid giving a duplicate `&mut` next time),
188+ // we can give out a reference to it.
189+ Some ( { ptr} . $into_ref( ) )
167190 }
168191 }
169192
0 commit comments