@@ -2152,6 +2152,7 @@ impl<T> Vec<T> {
21522152 del : 0 ,
21532153 old_len,
21542154 pred : filter,
2155+ panic_flag : false ,
21552156 }
21562157 }
21572158}
@@ -2779,10 +2780,20 @@ pub struct DrainFilter<'a, T, F>
27792780 where F : FnMut ( & mut T ) -> bool ,
27802781{
27812782 vec : & ' a mut Vec < T > ,
2783+ /// The index of the item that will be inspected by the next call to `next`.
27822784 idx : usize ,
2785+ /// The number of items that have been drained (removed) thus far.
27832786 del : usize ,
2787+ /// The original length of `vec` prior to draining.
27842788 old_len : usize ,
2789+ /// The filter test predicate.
27852790 pred : F ,
2791+ /// A flag that indicates a panic has occured in the filter test prodicate.
2792+ /// This is used as a hint in the drop implmentation to prevent consumption
2793+ /// of the remainder of the `DrainFilter`. Any unprocessed items will be
2794+ /// backshifted in the `vec`, but no further items will be dropped or
2795+ /// tested by the filter predicate.
2796+ panic_flag : bool ,
27862797}
27872798
27882799#[ unstable( feature = "drain_filter" , reason = "recently added" , issue = "43244" ) ]
@@ -2793,20 +2804,23 @@ impl<T, F> Iterator for DrainFilter<'_, T, F>
27932804
27942805 fn next ( & mut self ) -> Option < T > {
27952806 unsafe {
2796- while self . idx != self . old_len {
2807+ while self . idx < self . old_len {
27972808 let i = self . idx ;
2798- self . idx += 1 ;
27992809 let v = slice:: from_raw_parts_mut ( self . vec . as_mut_ptr ( ) , self . old_len ) ;
2800- if ( self . pred ) ( & mut v[ i] ) {
2810+ self . panic_flag = true ;
2811+ let drained = ( self . pred ) ( & mut v[ i] ) ;
2812+ self . panic_flag = false ;
2813+ // Update the index *after* the predicate is called. If the index
2814+ // is updated prior and the predicate panics, the element at this
2815+ // index would be leaked.
2816+ self . idx += 1 ;
2817+ if drained {
28012818 self . del += 1 ;
28022819 return Some ( ptr:: read ( & v[ i] ) ) ;
28032820 } else if self . del > 0 {
28042821 let del = self . del ;
28052822 let src: * const T = & v[ i] ;
28062823 let dst: * mut T = & mut v[ i - del] ;
2807- // This is safe because self.vec has length 0
2808- // thus its elements will not have Drop::drop
2809- // called on them in the event of a panic.
28102824 ptr:: copy_nonoverlapping ( src, dst, 1 ) ;
28112825 }
28122826 }
@@ -2824,9 +2838,46 @@ impl<T, F> Drop for DrainFilter<'_, T, F>
28242838 where F : FnMut ( & mut T ) -> bool ,
28252839{
28262840 fn drop ( & mut self ) {
2827- self . for_each ( drop) ;
2828- unsafe {
2829- self . vec . set_len ( self . old_len - self . del ) ;
2841+ struct BackshiftOnDrop < ' a , ' b , T , F >
2842+ where
2843+ F : FnMut ( & mut T ) -> bool ,
2844+ {
2845+ drain : & ' b mut DrainFilter < ' a , T , F > ,
2846+ }
2847+
2848+ impl < ' a , ' b , T , F > Drop for BackshiftOnDrop < ' a , ' b , T , F >
2849+ where
2850+ F : FnMut ( & mut T ) -> bool
2851+ {
2852+ fn drop ( & mut self ) {
2853+ unsafe {
2854+ if self . drain . idx < self . drain . old_len && self . drain . del > 0 {
2855+ // This is a pretty messed up state, and there isn't really an
2856+ // obviously right thing to do. We don't want to keep trying
2857+ // to execute `pred`, so we just backshift all the unprocessed
2858+ // elements and tell the vec that they still exist. The backshift
2859+ // is required to prevent a double-drop of the last successfully
2860+ // drained item prior to a panic in the predicate.
2861+ let ptr = self . drain . vec . as_mut_ptr ( ) ;
2862+ let src = ptr. add ( self . drain . idx ) ;
2863+ let dst = src. sub ( self . drain . del ) ;
2864+ let tail_len = self . drain . old_len - self . drain . idx ;
2865+ src. copy_to ( dst, tail_len) ;
2866+ }
2867+ self . drain . vec . set_len ( self . drain . old_len - self . drain . del ) ;
2868+ }
2869+ }
2870+ }
2871+
2872+ let backshift = BackshiftOnDrop {
2873+ drain : self
2874+ } ;
2875+
2876+ // Attempt to consume any remaining elements if the filter predicate
2877+ // has not yet panicked. We'll backshift any remaining elements
2878+ // whether we've already panicked or if the consumption here panics.
2879+ if !backshift. drain . panic_flag {
2880+ backshift. drain . for_each ( drop) ;
28302881 }
28312882 }
28322883}
0 commit comments