Skip to content

Commit a4e0832

Browse files
committed
Fix LinkedList::CursorMut::pop_front to correctly update index
When `pop_front` was called while the cursor pointed to the front element, `move_next` incremented the index but it was never decremented afterwards, causing the index to incorrectly report 1 instead of 0. Always decrement the index after popping from front using `saturating_sub` to handle edge cases safely. Fixes #147616
1 parent 31010ca commit a4e0832

File tree

2 files changed

+58
-2
lines changed

2 files changed

+58
-2
lines changed

library/alloc/src/collections/linked_list.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,9 +1855,11 @@ impl<'a, T, A: Allocator> CursorMut<'a, T, A> {
18551855
// node at index 0, which is expected.
18561856
if self.list.head == self.current {
18571857
self.move_next();
1858-
} else {
1859-
self.index -= 1;
18601858
}
1859+
// An element was removed before (or at) our current position, so
1860+
// the index must be decremented. `saturating_sub` handles the
1861+
// ghost node case where index could be 0.
1862+
self.index = self.index.saturating_sub(1);
18611863
self.list.pop_front()
18621864
}
18631865
}

library/alloc/src/collections/linked_list/tests.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,60 @@ fn test_cursor_pop_front_back() {
748748
assert_eq!(c.index, 2);
749749
}
750750

751+
#[test]
752+
fn test_cursor_pop_front_index() {
753+
// Regression test for issue #147616: `pop_front` was not correctly
754+
// updating the cursor index when the cursor was pointing to the front.
755+
756+
// Test case 1: pop_front when cursor is not at front, then at front
757+
let mut ll: LinkedList<u32> = LinkedList::new();
758+
ll.extend(&[0, 1, 2]);
759+
let mut c = ll.cursor_front_mut();
760+
761+
c.move_next();
762+
assert_eq!(c.index(), Some(1));
763+
assert_eq!(c.current(), Some(&mut 1));
764+
765+
// Pop front when cursor is not at front - index should decrement
766+
c.pop_front();
767+
assert_eq!(c.index(), Some(0));
768+
assert_eq!(c.current(), Some(&mut 1));
769+
770+
// Now cursor is at front, pop_front again - index should remain 0
771+
c.pop_front();
772+
assert_eq!(c.index(), Some(0));
773+
assert_eq!(c.current(), Some(&mut 2));
774+
check_links(&ll);
775+
776+
// Test case 2: minimal reproduction - cursor at front, pop_front
777+
let mut ll: LinkedList<u32> = LinkedList::new();
778+
ll.extend(&[0, 1]);
779+
let mut c = ll.cursor_front_mut();
780+
781+
assert_eq!(c.index(), Some(0));
782+
assert_eq!(c.current(), Some(&mut 0));
783+
784+
// Pop front when cursor is at front - should move to next and index stays 0
785+
c.pop_front();
786+
assert_eq!(c.index(), Some(0));
787+
assert_eq!(c.current(), Some(&mut 1));
788+
check_links(&ll);
789+
790+
// Test case 3: single element list
791+
let mut ll: LinkedList<u32> = LinkedList::new();
792+
ll.push_back(42);
793+
let mut c = ll.cursor_front_mut();
794+
795+
assert_eq!(c.index(), Some(0));
796+
assert_eq!(c.current(), Some(&mut 42));
797+
798+
// Pop the only element - cursor should be at ghost node with index 0
799+
c.pop_front();
800+
assert_eq!(c.index(), None);
801+
assert_eq!(c.current(), None);
802+
check_links(&ll);
803+
}
804+
751805
#[test]
752806
fn test_extend_ref() {
753807
let mut a = LinkedList::new();

0 commit comments

Comments
 (0)