pub struct GenericList<T: Clone + 'static, P: PointerFamily = RcPointer, const N: usize = 256, const G: usize = 1, D: DropHandler<Self> = DefaultDropHandler>(/* private fields */);Expand description
A persistent list.
This list is suitable for either a single threaded or multi threaded environment. The list accepts the smart pointer that you would like to use as a type parameter. There are sensible type aliases for implementations that you can use:
SharedList is simply a type alias for GenericList<T, ArcPointer, 256, 1>, which is both Send + Sync
Similarly, List is just a type alias for GenericList<T, RcPointer, 256, 1>. SharedVList and
VList are type aliases, as well, using the same backing of GenericList, however they have a growth factor of 2 - meaning
bucket sizes will grow exponentially.
It’s implemented as an unrolled linked list, which is a single linked list which stores a variable
amount of elements in each node. The capacity of any individual node for now is set to to be N elements, which means that until more than N elements
are cons’d onto a list, it will remain a vector under the hood. By default, N is sset to 256. There is also a growth rate, G, which describes how
each successive node will grow in size. With N = 2, and G = 2, the list will look something like this:
[0, 1, 2, 3, 4, 5, 6, 7] -> [8, 9, 10, 11] -> [12, 13]
The list is also designed to leverage in place mutations whenever possible - if the number of references pointing to either a cell containing a vector or the shared vector is one, then that mutation is done in place. Otherwise, it is copy-on-write, maintaining our persistent invariant.
§Performance Notes
The algorithmic complexity of an unrolled linked list matches that of a normal linked list - however in practice we have a decrease by the factor of the capacity of a node that gives us practical performance wins. For a list that is fully filled, iteration over nodes becomes O(n / N), rather than the typical O(n). If the growth rate is set to 2 (or more), over individual nodes becomes O(log(n)) - which means indexing or finding the last element is O(log(n)) as well. In addition, the unrolled linked list is able to avoid the costly cache misses that a typical linked list suffers from, seeing very realistic performance gains.
Let n be the number of elements in the list, and m is the capacity of a node. In the worst case, a node will be on average half filled. In the best case, all nodes are completely full. This means for operations that for a normal linked list may take linear time Θ(n), we get a constant factor decrease of either a factor of m or m / 2. Similarly, we will see O(log(n)) performance characteristics if the growth rate is set to be larger than 1.
Implementations§
Source§impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> GenericList<T, P, N, G, D>
impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> GenericList<T, P, N, G, D>
Sourcepub fn new_with_capacity() -> Self
pub fn new_with_capacity() -> Self
Constructs an empty list with capacity N
Sourcepub fn strong_count(&self) -> usize
pub fn strong_count(&self) -> usize
Get the number of strong references pointing to this list
Time: O(1)
Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Get the length of the list
§Examples
let list = list![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
assert_eq!(list.len(), 10);Sourcepub fn reverse(self) -> Self
pub fn reverse(self) -> Self
Reverses the input list and returns a new list
§Examples
let list = list![1, 2, 3, 4, 5].reverse();
assert_eq!(list, list![5, 4, 3, 2, 1])Sourcepub fn last(&self) -> Option<&T>
pub fn last(&self) -> Option<&T>
Get the last element of the list. Returns None if the list is empty.
§Examples
let list = list![1, 2, 3, 4, 5];
assert_eq!(list.last().cloned(), Some(5));Sourcepub fn car(&self) -> Option<T>
pub fn car(&self) -> Option<T>
Get the first element of the list. Returns None if the list is empty.
§Examples
let list = list![1, 2, 3, 4, 5];
let car = list.car();
assert_eq!(car, Some(1));
let list: List<usize> = list![];
let car = list.car();
assert!(car.is_none());Sourcepub fn first(&self) -> Option<&T>
pub fn first(&self) -> Option<&T>
Returns a reference to the first element of the list. Returns None if the list is empty.
§Examples
let list = list![1, 2, 3, 4, 5];
let first = list.first().cloned();
assert_eq!(first, Some(1));
let list: List<usize> = list![];
let first = list.first();
assert!(first.is_none());Sourcepub fn cdr(&self) -> Option<GenericList<T, P, N, G, D>>
pub fn cdr(&self) -> Option<GenericList<T, P, N, G, D>>
Get the “rest” of the elements as a list, excluding the first element
§Examples
let list = list![1, 2, 3, 4, 5];
let cdr = list.cdr().unwrap();
assert_eq!(cdr, list![2, 3, 4, 5]);
let list = list![5];
let cdr = list.cdr();
assert!(cdr.is_none());Sourcepub fn rest(&self) -> Option<GenericList<T, P, N, G, D>>
pub fn rest(&self) -> Option<GenericList<T, P, N, G, D>>
Get the “rest” of the elements as a list.
Alias for cdr
Sourcepub fn cdr_mut(&mut self) -> Option<&mut Self>
pub fn cdr_mut(&mut self) -> Option<&mut Self>
Gets the cdr of the list, mutably. Returns None if the next is empty - otherwise updates self to be the rest
§Examples
let mut list = list![1, 2, 3, 4, 5];
list.cdr_mut().expect("This list has a tail");
assert_eq!(list, list![2, 3, 4, 5]);
let mut list = list![1, 2, 3];
assert!(list.cdr_mut().is_some());
assert_eq!(list, list![2, 3]);
assert!(list.cdr_mut().is_some());
assert_eq!(list, list![3]);
assert!(list.cdr_mut().is_none());
assert_eq!(list, list![]);Sourcepub fn rest_mut(&mut self) -> Option<&mut Self>
pub fn rest_mut(&mut self) -> Option<&mut Self>
Gets the rest of the list, mutably.
Alias for cdr_mut
Sourcepub fn cons(
value: T,
other: GenericList<T, P, N, G, D>,
) -> GenericList<T, P, N, G, D>
pub fn cons( value: T, other: GenericList<T, P, N, G, D>, ) -> GenericList<T, P, N, G, D>
Pushes an element onto the front of the list, returning a new list
§Examples
let list = List::cons(1, List::cons(2, List::cons(3, List::cons(4, List::new()))));
assert_eq!(list, list![1, 2, 3, 4]);Sourcepub fn cons_mut(&mut self, value: T)
pub fn cons_mut(&mut self, value: T)
Mutably pushes an element onto the front of the list, in place
§Examples
let mut list = list![];
list.cons_mut(3);
list.cons_mut(2);
list.cons_mut(1);
list.cons_mut(0);
assert_eq!(list, list![0, 1, 2, 3])Sourcepub fn push_front(&mut self, value: T)
pub fn push_front(&mut self, value: T)
Alias for cons_mut
§Examples
let mut list = list![];
list.push_front(3);
list.push_front(2);
list.push_front(1);
list.push_front(0);
assert_eq!(list, list![0, 1, 2, 3])Sourcepub fn pop_front(&mut self) -> Option<T>
pub fn pop_front(&mut self) -> Option<T>
Mutably pop the first value off of the list
§Examples
let mut list = list![1, 2, 3];
assert_eq!(list.pop_front().unwrap(), 1);
assert_eq!(list.pop_front().unwrap(), 2);
assert_eq!(list.pop_front().unwrap(), 3);
assert!(list.pop_front().is_none())Sourcepub fn push_back(&mut self, value: T)
pub fn push_back(&mut self, value: T)
Push one value to the back of the list
Time: O(n)
§Examples
let mut list = list![];
list.push_back(0);
list.push_back(1);
list.push_back(2);
list.push_back(3);
assert_eq!(list, list![0, 1, 2, 3])Sourcepub fn take(&self, count: usize) -> Self
pub fn take(&self, count: usize) -> Self
Construct a new list from the first count elements from the current list
§Examples
let list = list![0, 1, 2, 3, 4, 5];
let new_list = list.take(3);
assert_eq!(new_list, list![0, 1, 2]);Sourcepub fn tail(&self, len: usize) -> Option<Self>
pub fn tail(&self, len: usize) -> Option<Self>
Returns the list after the first len elements of lst.
If the list has fewer then len elements, then this returns None.
§Examples
let list = list![0, 1, 2, 3, 4, 5];
let new_list = list.tail(2);
assert_eq!(new_list.unwrap(), list![2, 3, 4, 5]);
let no_list = list.tail(100);
assert!(no_list.is_none())Sourcepub fn get(&self, index: usize) -> Option<&T>
pub fn get(&self, index: usize) -> Option<&T>
Get a reference to the value at index index in a list.
Returns None if the index is out of bounds.
Sourcepub fn append(self, other: Self) -> Self
pub fn append(self, other: Self) -> Self
Append the list other to the end of the current list. Returns a new list.
§Examples
let left = list![1usize, 2, 3];
let right = list![4usize, 5, 6];
assert_eq!(left.append(right), list![1, 2, 3, 4, 5, 6])Sourcepub fn append_mut(&mut self, other: Self)
pub fn append_mut(&mut self, other: Self)
Append the list ‘other’ to the end of the current list in place.
§Examples
let mut left = list![1usize, 2, 3];
let right = list![4usize, 5, 6];
left.append_mut(right);
assert_eq!(left, list![1, 2, 3, 4, 5, 6])Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Checks whether a list is empty
§Examples
let mut list = List::new();
assert!(list.is_empty());
list.cons_mut("applesauce");
assert!(!list.is_empty());Trait Implementations§
Source§impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<GenericList<T, P, N, G, D>>> Add for &GenericList<T, P, N, G, D>
impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<GenericList<T, P, N, G, D>>> Add for &GenericList<T, P, N, G, D>
Source§impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> Add for GenericList<T, P, N, G, D>
impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> Add for GenericList<T, P, N, G, D>
Source§impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> Clone for GenericList<T, P, N, G, D>
impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> Clone for GenericList<T, P, N, G, D>
Source§impl<T: Clone + Debug, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> Debug for GenericList<T, P, N, G, D>
impl<T: Clone + Debug, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> Debug for GenericList<T, P, N, G, D>
Source§impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> Default for GenericList<T, P, N, G, D>
impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> Default for GenericList<T, P, N, G, D>
Source§impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> Drop for GenericList<T, P, N, G, D>
impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> Drop for GenericList<T, P, N, G, D>
Source§impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> Extend<T> for GenericList<T, P, N, G, D>
impl<T: Clone, P: PointerFamily, const N: usize, const G: usize, D: DropHandler<Self>> Extend<T> for GenericList<T, P, N, G, D>
Source§fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I)
fn extend<I: IntoIterator<Item = T>>(&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)