Repository: rust-lang/rust
Pull request: More informative `Debug for vec::ExtractIf`

While working on rust-lang/rust#154318, I've realized that `vec::ExtractIf` actually maintains a valid prefix and a valid suffix in the underlying vector at all times with all the temporarily-invalid elements being only in the middle.

So why not make the `Debug` output more informative? I guess we could even expose something like `get_retained_mut(&mut self) -> &mut [T]` and `get_remainder_mut(&mut self) -> &mut [T]`, but that would add new backwards compatibility burden, unlike the `Debug` implementation, which (IIRC) can be changed at any time.

Changed files:
filename: library/alloc/src/vec/extract_if.rs
patch: @@ -130,20 +130,25 @@ where
     A: Allocator,
 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let peek = if self.idx < self.end {
-            // This has to use pointer arithmetic as `self.vec[self.idx]` or
-            // `self.vec.get_unchecked(self.idx)` wouldn't work since we
-            // temporarily set the length of `self.vec` to zero.
-            //
-            // SAFETY:
-            // Since `self.idx` is smaller than `self.end` and `self.end` is
-            // smaller than `self.old_len`, `idx` is valid for indexing the
-            // buffer. Also, per the invariant of `self.idx`, this element
-            // has not been inspected/moved out yet.
-            Some(unsafe { &*self.vec.as_ptr().add(self.idx) })
-        } else {
-            None
-        };
-        f.debug_struct("ExtractIf").field("peek", &peek).finish_non_exhaustive()
+        // We have to use pointer arithmetics here,
+        // because the length of `self.vec` is temporarily set to 0.
+        let start = self.vec.as_ptr();
+
+        // SAFETY: we always keep first `self.idx - self.del` elements valid.
+        let retained = unsafe { slice::from_raw_parts(start, self.idx - self.del) };
+
+        // SAFETY: we have not yet touched elements starting at `self.idx`.
+        let valid_tail =
+            unsafe { slice::from_raw_parts(start.add(self.idx), self.old_len - self.idx) };
+
+        // SAFETY: `end - idx <= old_len - idx`, because `end <= old_len`. Also `idx <= end` by invariant.
+        let (remainder, skipped_tail) =
+            unsafe { valid_tail.split_at_unchecked(self.end - self.idx) };
+
+        f.debug_struct("ExtractIf")
+            .field("retained", &retained)
+            .field("remainder", &remainder)
+            .field("skipped_tail", &skipped_tail)
+            .finish_non_exhaustive()
     }
 }

filename: library/alloctests/tests/vec.rs
patch: @@ -1651,13 +1651,17 @@ fn extract_if_unconsumed() {
 
 #[test]
 fn extract_if_debug() {
-    let mut vec = vec![1, 2];
-    let mut drain = vec.extract_if(.., |&mut x| x % 2 != 0);
-    assert!(format!("{drain:?}").contains("Some(1)"));
-    drain.next();
-    assert!(format!("{drain:?}").contains("Some(2)"));
-    drain.next();
-    assert!(format!("{drain:?}").contains("None"));
+    let mut vec = vec![1, 2, 3, 4, 5, 6, 7, 8];
+    let mut drain = vec.extract_if(1..5, |&mut x| x % 2 != 0);
+    assert_eq!(
+        format!("{drain:?}"),
+        "ExtractIf { retained: [1], remainder: [2, 3, 4, 5], skipped_tail: [6, 7, 8], .. }"
+    );
+    drain.next().unwrap();
+    assert_eq!(
+        format!("{drain:?}"),
+        "ExtractIf { retained: [1, 2], remainder: [4, 5], skipped_tail: [6, 7, 8], .. }"
+    );
 }
 
 #[test]