@@ -154,16 +154,39 @@ macro_rules! iterator {
154
154
155
155
#[ inline]
156
156
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.
158
159
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!)
161
164
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 ) ) ;
164
175
} 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 ) ;
166
185
}
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( ) )
167
190
}
168
191
}
169
192
0 commit comments