@@ -329,6 +329,7 @@ where
329329 /// my_handle -= 1;
330330 /// }
331331 /// ```
332+ /// See [`repair_handles`](Self::repair_handles) for a helper that automates this.
332333 pub fn remove < Q > ( & mut self , item : & Q ) -> Option < ( H , T ) >
333334 where
334335 T : Borrow < Q > ,
@@ -360,11 +361,56 @@ where
360361 ///
361362 /// Any existing handle `h` where `h > handle` must be decremented by 1 to
362363 /// remain valid.
364+ /// See [`repair_handles`](Self::repair_handles) for a helper that automates this.
363365 pub fn remove_handle ( & mut self , handle : H ) -> Option < T > {
364366 let idx = usize:: try_from ( handle) . ok ( ) ?;
365367 self . items . shift_remove_index ( idx)
366368 }
367369
370+ /// A helper to update a collection of handles after a removal.
371+ ///
372+ /// When you call `remove`, handles greater than the removed index become invalid.
373+ /// This helper iterates over your collection of handles and decrements those
374+ /// that need to shift down, restoring their validity.
375+ ///
376+ /// # Generic Support
377+ ///
378+ /// This accepts any iterator that yields `&mut H`. This means it works with:
379+ /// - `&mut [H]` (slices and vectors of handles)
380+ /// - `.iter_mut()` on custom collections
381+ /// - `.iter_mut().map(|item| &mut item.id)` for structs containing handles
382+ ///
383+ /// # Example
384+ ///
385+ /// ```ignore
386+ /// let (removed_h, _) = interner.remove("ItemB").unwrap();
387+ ///
388+ /// // Fix a simple vector of handles
389+ /// interner.repair_handles(removed_h, &mut my_handle_vec);
390+ ///
391+ /// // Fix handles inside a custom struct
392+ /// interner.repair_handles(removed_h, my_structs.iter_mut().map(|s| &mut s.handle));
393+ /// ```
394+ pub fn repair_handles < ' a , I > ( & self , removed : H , handles : I )
395+ where
396+ I : IntoIterator < Item = & ' a mut H > ,
397+ H : ' a + PartialOrd ,
398+ {
399+ for h in handles {
400+ if * h > removed {
401+ // We rely on the generic H <-> usize conversion to perform the decrement.
402+ // We can safely unwrap here because:
403+ // 1. If h > removed, h must be >= 1.
404+ // 2. h - 1 is guaranteed to be a valid index that previously existed.
405+ if let Ok ( idx) = usize:: try_from ( * h)
406+ && let Ok ( shifted) = H :: try_from ( idx - 1 )
407+ {
408+ * h = shifted;
409+ }
410+ }
411+ }
412+ }
413+
368414 /// Current capacity, in number of items.
369415 #[ inline]
370416 pub fn capacity ( & self ) -> usize {
@@ -1049,4 +1095,81 @@ mod tests {
10491095 assert_eq ! ( handles[ 3 ] , 2 ) ;
10501096 assert_eq ! ( interner. resolve( handles[ 3 ] ) , Some ( & "D" . to_string( ) ) ) ;
10511097 }
1098+
1099+ #[ test]
1100+ fn test_remove_and_recover_handles_helper ( ) {
1101+ let mut interner = create_string_interner ( ) ;
1102+
1103+ let mut handles = alloc:: vec![
1104+ interner. intern_ref( "A" ) . unwrap( ) , // 0
1105+ interner. intern_ref( "B" ) . unwrap( ) , // 1
1106+ interner. intern_ref( "C" ) . unwrap( ) , // 2
1107+ interner. intern_ref( "D" ) . unwrap( ) , // 3
1108+ ] ;
1109+
1110+ // 1. Remove "B" (index 1).
1111+ let ( removed_handle, val) = interner. remove ( "B" ) . unwrap ( ) ;
1112+ assert_eq ! ( val, "B" ) ;
1113+
1114+ // 2. REPAIR AUTOMATICALLY
1115+ // We pass a mutable reference to the vector (which is IntoIterator)
1116+ interner. repair_handles ( removed_handle, & mut handles) ;
1117+
1118+ // 3. Verify
1119+ assert_eq ! ( interner. resolve( handles[ 0 ] ) , Some ( & "A" . to_string( ) ) ) ;
1120+ assert_eq ! ( interner. resolve( handles[ 1 ] ) , Some ( & "C" . to_string( ) ) ) ; // Was 1, still 1, now points to C
1121+ assert_eq ! ( interner. resolve( handles[ 2 ] ) , Some ( & "C" . to_string( ) ) ) ; // Was 2, fixed to 1, points to C
1122+ assert_eq ! ( interner. resolve( handles[ 3 ] ) , Some ( & "D" . to_string( ) ) ) ; // Was 3, fixed to 2, points to D
1123+ }
1124+
1125+ #[ test]
1126+ fn test_repair_handles_in_structs ( ) {
1127+ struct User {
1128+ name_handle : u32 ,
1129+ _score : i32 ,
1130+ }
1131+
1132+ let mut interner = create_string_interner ( ) ;
1133+ let h_a = interner. intern_ref ( "A" ) . unwrap ( ) ; // 0
1134+ let h_b = interner. intern_ref ( "B" ) . unwrap ( ) ; // 1
1135+ let h_c = interner. intern_ref ( "C" ) . unwrap ( ) ; // 2
1136+
1137+ let mut users = alloc:: vec![
1138+ User {
1139+ name_handle: h_a,
1140+ _score: 10 ,
1141+ } ,
1142+ User {
1143+ name_handle: h_b,
1144+ _score: 20 ,
1145+ } ,
1146+ User {
1147+ name_handle: h_c,
1148+ _score: 30 ,
1149+ } ,
1150+ ] ;
1151+
1152+ // Remove "A" (Handle 0). Everything > 0 should shift down.
1153+ let ( removed, _) = interner. remove ( "A" ) . unwrap ( ) ;
1154+
1155+ // Complex usage: map to the field
1156+ interner. repair_handles ( removed, users. iter_mut ( ) . map ( |u| & mut u. name_handle ) ) ;
1157+
1158+ // Validation
1159+ // A was removed.
1160+ // B (was 1) should become 0.
1161+ // C (was 2) should become 1.
1162+
1163+ assert_eq ! ( users[ 1 ] . name_handle, 0 ) ;
1164+ assert_eq ! (
1165+ interner. resolve( users[ 1 ] . name_handle) ,
1166+ Some ( & "B" . to_string( ) )
1167+ ) ;
1168+
1169+ assert_eq ! ( users[ 2 ] . name_handle, 1 ) ;
1170+ assert_eq ! (
1171+ interner. resolve( users[ 2 ] . name_handle) ,
1172+ Some ( & "C" . to_string( ) )
1173+ ) ;
1174+ }
10521175}
0 commit comments