@@ -107,9 +107,19 @@ fn test_zip_next_back_side_effects_exhausted() {
107107 iter. next ( ) ;
108108 iter. next ( ) ;
109109 iter. next ( ) ;
110- iter. next ( ) ;
110+ assert_eq ! ( iter. next( ) , None ) ;
111111 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+
113123 assert_eq ! ( b, vec![ 200 , 300 , 400 ] ) ;
114124}
115125
@@ -120,7 +130,8 @@ fn test_zip_cloned_sideffectful() {
120130
121131 for _ in xs. iter ( ) . cloned ( ) . zip ( ys. iter ( ) . cloned ( ) ) { }
122132
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) ) ;
124135 assert_eq ! ( & ys, & [ 1 , 1 ] [ ..] ) ;
125136
126137 let xs = [ CountClone :: new ( ) , CountClone :: new ( ) ] ;
@@ -139,7 +150,8 @@ fn test_zip_map_sideffectful() {
139150
140151 for _ in xs. iter_mut ( ) . map ( |x| * x += 1 ) . zip ( ys. iter_mut ( ) . map ( |y| * y += 1 ) ) { }
141152
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) ) ;
143155 assert_eq ! ( & ys, & [ 1 , 1 , 1 , 1 ] ) ;
144156
145157 let mut xs = [ 0 ; 4 ] ;
@@ -168,7 +180,8 @@ fn test_zip_map_rev_sideffectful() {
168180
169181 {
170182 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 ( ) ;
172185 it. next_back ( ) ;
173186 }
174187 assert_eq ! ( & xs, & [ 1 , 1 , 1 , 1 , 1 , 1 ] ) ;
@@ -211,9 +224,18 @@ fn test_zip_nth_back_side_effects_exhausted() {
211224 iter. next ( ) ;
212225 iter. next ( ) ;
213226 iter. next ( ) ;
214- iter. next ( ) ;
227+ assert_eq ! ( iter. next( ) , None ) ;
215228 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+
217239 assert_eq ! ( b, vec![ 200 , 300 , 400 ] ) ;
218240}
219241
@@ -237,32 +259,6 @@ fn test_zip_trusted_random_access_composition() {
237259 assert_eq ! ( z2. next( ) . unwrap( ) , ( ( 1 , 1 ) , 1 ) ) ;
238260}
239261
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-
266262#[ test]
267263fn test_double_ended_zip ( ) {
268264 let xs = [ 1 , 2 , 3 , 4 , 5 , 6 ] ;
@@ -275,6 +271,42 @@ fn test_double_ended_zip() {
275271 assert_eq ! ( it. next( ) , None ) ;
276272}
277273
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+
278310#[ test]
279311fn test_issue_82282 ( ) {
280312 fn overflowed_zip ( arr : & [ i32 ] ) -> impl Iterator < Item = ( i32 , & ( ) ) > {
0 commit comments