pub struct List<T: Linked<Links<T>> + ?Sized> { /* private fields */ }
Expand description
An intrusive doubly-linked list.
This data structure may be used as a first-in, first-out queue by using the
List::push_front
and List::pop_back
methods. It also supports
random-access removals using the List::remove
method. This makes the
List
type suitable for use in cases where elements must be able to drop
themselves while linked into a list.
This data structure can also be used as a stack or doubly-linked list by using
the List::pop_front
and List::push_back
methods.
The List
type is not a lock-free data structure, and can only be
modified through &mut
references.
In order to be part of a List
, a type T
must implement Linked
for
list::Links<T>
.
§Examples
Implementing the Linked
trait for an entry type:
use cordyceps::{
Linked,
list::{self, List},
};
// This example uses the Rust standard library for convenience, but
// the doubly-linked list itself does not require std.
use std::{pin::Pin, ptr::{self, NonNull}, thread, sync::Arc};
/// A simple queue entry that stores an `i32`.
#[derive(Debug, Default)]
struct Entry {
links: list::Links<Entry>,
val: i32,
}
// Implement the `Linked` trait for our entry type so that it can be used
// as a queue entry.
unsafe impl Linked<list::Links<Entry>> for Entry {
// In this example, our entries will be "owned" by a `Box`, but any
// heap-allocated type that owns an element may be used.
//
// An element *must not* move while part of an intrusive data
// structure. In many cases, `Pin` may be used to enforce this.
type Handle = Pin<Box<Self>>;
/// Convert an owned `Handle` into a raw pointer
fn into_ptr(handle: Pin<Box<Entry>>) -> NonNull<Entry> {
unsafe { NonNull::from(Box::leak(Pin::into_inner_unchecked(handle))) }
}
/// Convert a raw pointer back into an owned `Handle`.
unsafe fn from_ptr(ptr: NonNull<Entry>) -> Pin<Box<Entry>> {
// Safety: if this function is only called by the linked list
// implementation (and it is not intended for external use), we can
// expect that the `NonNull` was constructed from a reference which
// was pinned.
//
// If other callers besides `List`'s internals were to call this on
// some random `NonNull<Entry>`, this would not be the case, and
// this could be constructing an erroneous `Pin` from a referent
// that may not be pinned!
Pin::new_unchecked(Box::from_raw(ptr.as_ptr()))
}
/// Access an element's `Links`.
unsafe fn links(target: NonNull<Entry>) -> NonNull<list::Links<Entry>> {
// Using `ptr::addr_of_mut!` permits us to avoid creating a temporary
// reference without using layout-dependent casts.
let links = ptr::addr_of_mut!((*target.as_ptr()).links);
// `NonNull::new_unchecked` is safe to use here, because the pointer that
// we offset was not null, implying that the pointer produced by offsetting
// it will also not be null.
NonNull::new_unchecked(links)
}
}
impl Entry {
fn new(val: i32) -> Self {
Self {
val,
..Self::default()
}
}
}
Using a List
as a first-in, first-out (FIFO) queue with
List::push_back
and List::pop_front
:
// Now that we've implemented the `Linked` trait for our `Entry` type, we can
// create a `List` of entries:
let mut list = List::<Entry>::new();
// Push some entries to the list:
for i in 0..5 {
list.push_back(Box::pin(Entry::new(i)));
}
// The list is a doubly-ended queue. We can use the `pop_front` method with
// `push_back` to dequeue elements in FIFO order:
for i in 0..5 {
let entry = list.pop_front()
.expect("the list should have 5 entries in it");
assert_eq!(entry.val, i, "entries are dequeued in FIFO order");
}
assert!(list.is_empty());
Using a List
as a last-in, first-out (LIFO) stack with
List::push_back
and List::pop_back
:
let mut list = List::<Entry>::new();
// Push some entries to the list:
for i in 0..5 {
list.push_back(Box::pin(Entry::new(i)));
}
// Note that we have reversed the direction of the iterator, since
// we are popping from the *back* of the list:
for i in (0..5).into_iter().rev() {
let entry = list.pop_back()
.expect("the list should have 5 entries in it");
assert_eq!(entry.val, i, "entries are dequeued in LIFO order");
}
assert!(list.is_empty());
Implementations§
Source§impl<T: Linked<Links<T>> + ?Sized> List<T>
impl<T: Linked<Links<T>> + ?Sized> List<T>
Sourcepub fn append(&mut self, other: &mut Self)
pub fn append(&mut self, other: &mut Self)
Moves all elements from other
to the end of the list.
This reuses all the nodes from other
and moves them into self
. After
this operation, other
becomes empty.
This operation should complete in O(1) time and O(1) memory.
Sourcepub fn try_split_off(&mut self, at: usize) -> Option<Self>
pub fn try_split_off(&mut self, at: usize) -> Option<Self>
Sourcepub fn split_off(&mut self, at: usize) -> Self
pub fn split_off(&mut self, at: usize) -> Self
Split the list into two at the given index (inclusive).
Every element after the given index, including the node at that index, is removed from this list, and returned as a new list.
This operation should complete in O(n) time.
§Returns
A new List
<T>
containing every element after the index at
in
this list.
§Panics
If at > self.len()
.
Sourcepub fn assert_valid(&self)
pub fn assert_valid(&self)
Asserts as many of the linked list’s invariants as possible.
Sourcepub fn pop_back(&mut self) -> Option<T::Handle>
pub fn pop_back(&mut self) -> Option<T::Handle>
Sourcepub fn pop_front(&mut self) -> Option<T::Handle>
pub fn pop_front(&mut self) -> Option<T::Handle>
Sourcepub fn push_back(&mut self, item: T::Handle)
pub fn push_back(&mut self, item: T::Handle)
Appends an item to the tail of the list.
This operation should complete in O(1) time.
This takes a Handle
that owns the appended item
. While the element
is in the list, it is owned by the list, and will be dropped when the
list is dropped. If the element is removed or otherwise unlinked from
the list, ownership is assigned back to the Handle
.
Sourcepub fn push_front(&mut self, item: T::Handle)
pub fn push_front(&mut self, item: T::Handle)
Appends an item to the head of the list.
This operation should complete in O(1) time.
This takes a Handle
that owns the appended item
. While the element
is in the list, it is owned by the list, and will be dropped when the
list is dropped. If the element is removed or otherwise unlinked from
the list, ownership is assigned back to the Handle
.
Sourcepub fn front(&self) -> Option<Pin<&T>>
pub fn front(&self) -> Option<Pin<&T>>
Sourcepub fn front_mut(&mut self) -> Option<Pin<&mut T>>
pub fn front_mut(&mut self) -> Option<Pin<&mut T>>
Sourcepub fn back(&self) -> Option<Pin<&T>>
pub fn back(&self) -> Option<Pin<&T>>
Sourcepub fn back_mut(&mut self) -> Option<Pin<&mut T>>
pub fn back_mut(&mut self) -> Option<Pin<&mut T>>
Sourcepub unsafe fn remove(&mut self, item: NonNull<T>) -> Option<T::Handle>
pub unsafe fn remove(&mut self, item: NonNull<T>) -> Option<T::Handle>
Remove an arbitrary node from the list.
This operation should complete in O(1) time.
This returns a Handle
that owns the popped element. Dropping the
Handle
will drop the element.
§Returns
Some
(T::Handle)
containing aHandle
that ownsitem
, ifitem
is currently linked into this list.None
ifitem
is not an element of this list.
§Safety
The caller must ensure that the removed node is an element of this linked list, and not any other linked list.
Sourcepub fn cursor_front_mut(&mut self) -> CursorMut<'_, T> ⓘ
pub fn cursor_front_mut(&mut self) -> CursorMut<'_, T> ⓘ
Sourcepub fn cursor_back_mut(&mut self) -> CursorMut<'_, T> ⓘ
pub fn cursor_back_mut(&mut self) -> CursorMut<'_, T> ⓘ
Sourcepub fn cursor_front(&self) -> Cursor<'_, T> ⓘ
pub fn cursor_front(&self) -> Cursor<'_, T> ⓘ
Sourcepub fn cursor_back(&self) -> Cursor<'_, T> ⓘ
pub fn cursor_back(&self) -> Cursor<'_, T> ⓘ
Sourcepub fn iter(&self) -> Iter<'_, T> ⓘ
pub fn iter(&self) -> Iter<'_, T> ⓘ
Returns an iterator over the items in this list, by reference.
Sourcepub fn iter_mut(&mut self) -> IterMut<'_, T> ⓘ
pub fn iter_mut(&mut self) -> IterMut<'_, T> ⓘ
Returns an iterator over the items in this list, by mutable reference.
Sourcepub fn drain_filter<F>(&mut self, pred: F) -> DrainFilter<'_, T, F> ⓘ
pub fn drain_filter<F>(&mut self, pred: F) -> DrainFilter<'_, T, F> ⓘ
Returns an iterator which uses a closure to determine if an element should be removed from the list.
If the closure returns true
, then the element is removed and yielded.
If the closure returns false
, the element will remain in the list and
will not be yielded by the iterator.
Note that unlike the drain_filter
method on
[std::collections::LinkedList
], the closure is not permitted to
mutate the elements of the list, as a mutable reference could be used to
improperly unlink list nodes.
Trait Implementations§
Source§impl<T> Extend<<T as Linked<Links<T>>>::Handle> for List<T>
impl<T> Extend<<T as Linked<Links<T>>>::Handle> for List<T>
Source§fn extend<I: IntoIterator<Item = T::Handle>>(&mut self, iter: I)
fn extend<I: IntoIterator<Item = T::Handle>>(&mut self, iter: I)
Source§fn extend_one(&mut self, item: A)
fn extend_one(&mut self, item: A)
extend_one
)Source§fn extend_reserve(&mut self, additional: usize)
fn extend_reserve(&mut self, additional: usize)
extend_one
)