@@ -1371,21 +1371,78 @@ impl<T, A: Allocator> Vec<T, A> {
1371
1371
F : FnMut ( & T ) -> bool ,
1372
1372
{
1373
1373
let len = self . len ( ) ;
1374
- let mut del = 0 ;
1375
- {
1376
- let v = & mut * * self ;
1377
-
1378
- for i in 0 ..len {
1379
- if !f ( & v[ i] ) {
1380
- del += 1 ;
1381
- } else if del > 0 {
1382
- v. swap ( i - del, i) ;
1374
+ // Avoid double drop if the drop guard is not executed,
1375
+ // since we may make some holes during the process.
1376
+ unsafe { self . set_len ( 0 ) } ;
1377
+
1378
+ // Vec: [Kept, Kept, Hole, Hole, Hole, Hole, Unchecked, Unchecked]
1379
+ // |<- processed len ->| ^- next to check
1380
+ // |<- deleted cnt ->|
1381
+ // |<- original_len ->|
1382
+ // Kept: Elements which predicate returns true on.
1383
+ // Hole: Moved or dropped element slot.
1384
+ // Unchecked: Unchecked valid elements.
1385
+ //
1386
+ // This drop guard will be invoked when predicate or `drop` of element panicked.
1387
+ // It shifts unchecked elements to cover holes and `set_len` to the correct length.
1388
+ // In cases when predicate and `drop` never panick, it will be optimized out.
1389
+ struct BackshiftOnDrop < ' a , T , A : Allocator > {
1390
+ v : & ' a mut Vec < T , A > ,
1391
+ processed_len : usize ,
1392
+ deleted_cnt : usize ,
1393
+ original_len : usize ,
1394
+ }
1395
+
1396
+ impl < T , A : Allocator > Drop for BackshiftOnDrop < ' _ , T , A > {
1397
+ fn drop ( & mut self ) {
1398
+ if self . deleted_cnt > 0 {
1399
+ // SAFETY: Fill the hole of dropped or moved
1400
+ unsafe {
1401
+ ptr:: copy (
1402
+ self . v . as_ptr ( ) . offset ( self . processed_len as isize ) ,
1403
+ self . v
1404
+ . as_mut_ptr ( )
1405
+ . offset ( self . processed_len as isize - self . deleted_cnt as isize ) ,
1406
+ self . original_len - self . processed_len ,
1407
+ ) ;
1408
+ self . v . set_len ( self . original_len - self . deleted_cnt ) ;
1409
+ }
1383
1410
}
1384
1411
}
1385
1412
}
1386
- if del > 0 {
1387
- self . truncate ( len - del) ;
1413
+
1414
+ let mut guard = BackshiftOnDrop {
1415
+ v : self ,
1416
+ processed_len : 0 ,
1417
+ deleted_cnt : 0 ,
1418
+ original_len : len,
1419
+ } ;
1420
+
1421
+ let mut del = 0usize ;
1422
+ for i in 0 ..len {
1423
+ // SAFETY: Unchecked element must be valid.
1424
+ let cur = unsafe { & mut * guard. v . as_mut_ptr ( ) . offset ( i as isize ) } ;
1425
+ if !f ( cur) {
1426
+ del += 1 ;
1427
+ // Advance early to avoid double drop if `drop_in_place` panicked.
1428
+ guard. processed_len = i + 1 ;
1429
+ guard. deleted_cnt = del;
1430
+ // SAFETY: We never touch this element again after dropped.
1431
+ unsafe { ptr:: drop_in_place ( cur) } ;
1432
+ } else if del > 0 {
1433
+ // SAFETY: `del` > 0 so the hole slot must not overlap with current element.
1434
+ // We use copy for move, and never touch this element again.
1435
+ unsafe {
1436
+ let hole_slot = guard. v . as_mut_ptr ( ) . offset ( i as isize - del as isize ) ;
1437
+ ptr:: copy_nonoverlapping ( cur, hole_slot, 1 ) ;
1438
+ }
1439
+ guard. processed_len = i + 1 ;
1440
+ }
1388
1441
}
1442
+
1443
+ // All holes are at the end now. Simply cut them out.
1444
+ unsafe { guard. v . set_len ( len - del) } ;
1445
+ mem:: forget ( guard) ;
1389
1446
}
1390
1447
1391
1448
/// Removes all but the first of consecutive elements in the vector that resolve to the same
0 commit comments