From de41d84ddfda90c9cc439ca12fce455fe2f7aaf1 Mon Sep 17 00:00:00 2001 From: John-John Tedro Date: Sat, 25 Nov 2017 18:35:30 +0100 Subject: Introduce LinkedList::remove_if --- src/liballoc/linked_list.rs | 79 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs index fac6acaca61..689a2671994 100644 --- a/src/liballoc/linked_list.rs +++ b/src/liballoc/linked_list.rs @@ -220,6 +220,28 @@ impl LinkedList { node }) } + + /// Unlinks the specified node from the current list. + /// + /// Warning: this will not check that the provided node belongs to the current list. + #[inline] + unsafe fn unlink_node(&mut self, mut node: Shared>) { + let node = node.as_mut(); + + match node.prev { + Some(mut prev) => prev.as_mut().next = node.next.clone(), + // this node is the head node + None => self.head = node.next.clone(), + }; + + match node.next { + Some(mut next) => next.as_mut().prev = node.prev.clone(), + // this node is the tail node + None => self.tail = node.prev.clone(), + }; + + self.len -= 1; + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -722,6 +744,50 @@ impl LinkedList { second_part } + /// Removes any element matching the given predicate. Returns the elements which were removed + /// in a new list. + /// + /// This operation should compute in O(n) time. + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_remove_if)] + /// + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// d.push_back(1); + /// d.push_back(2); + /// d.push_back(3); + /// assert_eq!(d.remove_if(|v| *v < 3).len(), 2); + /// assert_eq!(d.len(), 1); + /// ``` + #[unstable(feature = "linked_list_remove_if", + reason = "experimental method", + issue = "0")] + pub fn remove_if

(&mut self, predicate: P) -> LinkedList + where P: Fn(&T) -> bool + { + let mut deleted = LinkedList::new(); + + let mut it = self.head; + + while let Some(node) = it { + unsafe { + it = node.as_ref().next; + + if predicate(&node.as_ref().element) { + self.unlink_node(node); + // move the unlinked node into the deleted list. + deleted.push_back_node(Box::from_raw(node.as_ptr())); + } + } + } + + deleted + } + /// Returns a place for insertion at the front of the list. /// /// Using this method with placement syntax is equivalent to @@ -1502,4 +1568,17 @@ mod tests { } assert_eq!(i, v.len()); } + + #[test] + fn remove_if_test() { + let mut m: LinkedList = LinkedList::new(); + m.extend(&[1, 2, 3, 4, 5, 6]); + let deleted = m.remove_if(|v| *v < 4); + + check_links(&m); + check_links(&deleted); + + assert_eq!(deleted.into_iter().collect::>(), &[1, 2, 3]); + assert_eq!(m.into_iter().collect::>(), &[4, 5, 6]); + } } -- cgit 1.4.1-3-g733a5