@@ -107,9 +107,19 @@ fn test_zip_next_back_side_effects_exhausted() {
107
107
iter. next ( ) ;
108
108
iter. next ( ) ;
109
109
iter. next ( ) ;
110
- iter. next ( ) ;
110
+ assert_eq ! ( iter. next( ) , None ) ;
111
111
assert_eq ! ( iter. next_back( ) , None ) ;
112
- assert_eq ! ( a, vec![ 1 , 2 , 3 , 4 , 6 , 5 ] ) ;
112
+
113
+ assert ! ( a. starts_with( & [ 1 , 2 , 3 ] ) ) ;
114
+ let a_len = a. len ( ) ;
115
+ // Tail-side-effects of forward-iteration are "at most one" per next().
116
+ // And for reverse iteration we don't guarantee much either.
117
+ // But we can put some bounds on the possible behaviors.
118
+ assert ! ( a_len <= 6 ) ;
119
+ assert ! ( a_len >= 3 ) ;
120
+ a. sort ( ) ;
121
+ assert_eq ! ( a, & [ 1 , 2 , 3 , 4 , 5 , 6 ] [ ..a. len( ) ] ) ;
122
+
113
123
assert_eq ! ( b, vec![ 200 , 300 , 400 ] ) ;
114
124
}
115
125
@@ -120,7 +130,8 @@ fn test_zip_cloned_sideffectful() {
120
130
121
131
for _ in xs. iter ( ) . cloned ( ) . zip ( ys. iter ( ) . cloned ( ) ) { }
122
132
123
- assert_eq ! ( & xs, & [ 1 , 1 , 1 , 0 ] [ ..] ) ;
133
+ // Zip documentation permits either case.
134
+ assert ! ( [ & [ 1 , 1 , 1 , 0 ] , & [ 1 , 1 , 0 , 0 ] ] . iter( ) . any( |v| & xs == * v) ) ;
124
135
assert_eq ! ( & ys, & [ 1 , 1 ] [ ..] ) ;
125
136
126
137
let xs = [ CountClone :: new ( ) , CountClone :: new ( ) ] ;
@@ -139,7 +150,8 @@ fn test_zip_map_sideffectful() {
139
150
140
151
for _ in xs. iter_mut ( ) . map ( |x| * x += 1 ) . zip ( ys. iter_mut ( ) . map ( |y| * y += 1 ) ) { }
141
152
142
- assert_eq ! ( & xs, & [ 1 , 1 , 1 , 1 , 1 , 0 ] ) ;
153
+ // Zip documentation permits either case.
154
+ assert ! ( [ & [ 1 , 1 , 1 , 1 , 1 , 0 ] , & [ 1 , 1 , 1 , 1 , 0 , 0 ] ] . iter( ) . any( |v| & xs == * v) ) ;
143
155
assert_eq ! ( & ys, & [ 1 , 1 , 1 , 1 ] ) ;
144
156
145
157
let mut xs = [ 0 ; 4 ] ;
@@ -168,7 +180,8 @@ fn test_zip_map_rev_sideffectful() {
168
180
169
181
{
170
182
let mut it = xs. iter_mut ( ) . map ( |x| * x += 1 ) . zip ( ys. iter_mut ( ) . map ( |y| * y += 1 ) ) ;
171
- ( & mut it) . take ( 5 ) . count ( ) ;
183
+ // the current impl only trims the tails if the iterator isn't exhausted
184
+ ( & mut it) . take ( 3 ) . count ( ) ;
172
185
it. next_back ( ) ;
173
186
}
174
187
assert_eq ! ( & xs, & [ 1 , 1 , 1 , 1 , 1 , 1 ] ) ;
@@ -211,9 +224,18 @@ fn test_zip_nth_back_side_effects_exhausted() {
211
224
iter. next ( ) ;
212
225
iter. next ( ) ;
213
226
iter. next ( ) ;
214
- iter. next ( ) ;
227
+ assert_eq ! ( iter. next( ) , None ) ;
215
228
assert_eq ! ( iter. nth_back( 0 ) , None ) ;
216
- assert_eq ! ( a, vec![ 1 , 2 , 3 , 4 , 6 , 5 ] ) ;
229
+ assert ! ( a. starts_with( & [ 1 , 2 , 3 ] ) ) ;
230
+ let a_len = a. len ( ) ;
231
+ // Tail-side-effects of forward-iteration are "at most one" per next().
232
+ // And for reverse iteration we don't guarantee much either.
233
+ // But we can put some bounds on the possible behaviors.
234
+ assert ! ( a_len <= 6 ) ;
235
+ assert ! ( a_len >= 3 ) ;
236
+ a. sort ( ) ;
237
+ assert_eq ! ( a, & [ 1 , 2 , 3 , 4 , 5 , 6 ] [ ..a. len( ) ] ) ;
238
+
217
239
assert_eq ! ( b, vec![ 200 , 300 , 400 ] ) ;
218
240
}
219
241
@@ -237,32 +259,6 @@ fn test_zip_trusted_random_access_composition() {
237
259
assert_eq ! ( z2. next( ) . unwrap( ) , ( ( 1 , 1 ) , 1 ) ) ;
238
260
}
239
261
240
- #[ test]
241
- #[ cfg( panic = "unwind" ) ]
242
- fn test_zip_trusted_random_access_next_back_drop ( ) {
243
- use std:: panic:: { AssertUnwindSafe , catch_unwind} ;
244
-
245
- let mut counter = 0 ;
246
-
247
- let it = [ 42 ] . iter ( ) . map ( |e| {
248
- let c = counter;
249
- counter += 1 ;
250
- if c == 0 {
251
- panic ! ( "bomb" ) ;
252
- }
253
-
254
- e
255
- } ) ;
256
- let it2 = [ ( ) ; 0 ] . iter ( ) ;
257
- let mut zip = it. zip ( it2) ;
258
- catch_unwind ( AssertUnwindSafe ( || {
259
- zip. next_back ( ) ;
260
- } ) )
261
- . unwrap_err ( ) ;
262
- assert ! ( zip. next( ) . is_none( ) ) ;
263
- assert_eq ! ( counter, 1 ) ;
264
- }
265
-
266
262
#[ test]
267
263
fn test_double_ended_zip ( ) {
268
264
let xs = [ 1 , 2 , 3 , 4 , 5 , 6 ] ;
@@ -275,6 +271,42 @@ fn test_double_ended_zip() {
275
271
assert_eq ! ( it. next( ) , None ) ;
276
272
}
277
273
274
+ #[ test]
275
+ #[ cfg( panic = "unwind" ) ]
276
+ fn test_nested_zip_panic_safety ( ) {
277
+ use std:: panic:: { resume_unwind, catch_unwind} ;
278
+ use std:: panic:: AssertUnwindSafe ;
279
+ use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
280
+
281
+ let mut panic = true ;
282
+ let witness = [ 8 , 9 , 10 , 11 , 12 ] . map ( |i| ( i, AtomicUsize :: new ( 0 ) ) ) ;
283
+ let a = witness. as_slice ( ) . iter ( ) . map ( |e| {
284
+ e. 1 . fetch_add ( 1 , Ordering :: Relaxed ) ;
285
+ if panic {
286
+ panic = false ;
287
+ resume_unwind ( Box :: new ( ( ) ) )
288
+ }
289
+ e. 0
290
+ } ) ;
291
+ let b = [ 1 , 2 , 3 , 4 ] . as_slice ( ) . iter ( ) . copied ( ) ;
292
+ let c = [ 5 , 6 , 7 ] . as_slice ( ) . iter ( ) . copied ( ) ;
293
+ let ab = zip ( a, b) ;
294
+
295
+ let mut abc = zip ( ab, c) ;
296
+
297
+ assert_eq ! ( abc. len( ) , 3 ) ;
298
+ catch_unwind ( AssertUnwindSafe ( || abc. next_back ( ) ) ) . ok ( ) ;
299
+ assert_eq ! ( abc. len( ) , 2 ) ;
300
+ assert_eq ! ( abc. next( ) , Some ( ( ( 8 , 1 ) , 5 ) ) ) ;
301
+ assert_eq ! ( abc. next_back( ) , Some ( ( ( 9 , 2 ) , 6 ) ) ) ;
302
+ for ( i, ( _, w) ) in witness. iter ( ) . enumerate ( ) {
303
+ let v = w. load ( Ordering :: Relaxed ) ;
304
+ assert ! ( v <= 1 , "expected idx {i} to be visited at most once, actual: {v}" ) ;
305
+ }
306
+ // backwards trimming panicked and should only run once, so this one won't be visited.
307
+ assert_eq ! ( witness[ 3 ] . 1 . load( Ordering :: Relaxed ) , 0 ) ;
308
+ }
309
+
278
310
#[ test]
279
311
fn test_issue_82282 ( ) {
280
312
fn overflowed_zip ( arr : & [ i32 ] ) -> impl Iterator < Item = ( i32 , & ( ) ) > {
0 commit comments