Skip to content

Commit 4a7d0f5

Browse files
authored
Merge pull request #36 from orxfun/lifetime-ellisions
Lifetime elision fixes
2 parents b1bc306 + bec45f7 commit 4a7d0f5

25 files changed

+101
-103
lines changed

Cargo.toml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "orx-linked-list"
3-
version = "3.1.0"
3+
version = "3.2.0"
44
edition = "2021"
55
authors = ["orxfun <[email protected]>"]
66
description = "A linked list implementation with unique features and an extended list of constant time methods providing high performance traversals and mutations."
@@ -11,10 +11,10 @@ categories = ["data-structures", "rust-patterns", "no-std"]
1111

1212
[dependencies]
1313
orx-pseudo-default = { version = "1.4", default-features = false }
14-
orx-pinned-vec = "3.9"
15-
orx-split-vec = "3.9"
16-
orx-fixed-vec = "3.9"
17-
orx-selfref-col = "2.1"
14+
orx-pinned-vec = "3.11"
15+
orx-split-vec = "3.11"
16+
orx-fixed-vec = "3.11"
17+
orx-selfref-col = "2.2"
1818

1919
[dev-dependencies]
2020
clap = { version = "4.5.17", features = ["derive"] }

README.md

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ A linked list implementation with unique features and an extended list of consta
77

88
Both doubly and singly lists are provided as generic variants of the core struct `List`. It is sufficient to know the four variants:
99
* [`DoublyList`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyList.html) and [`SinglyList`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.SinglyList.html)
10-
* [`DoublyListLazy`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyListLazy.html) and [`SinglyListLazy`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.SinglyListLazy.html) -> lazy suffix corresponds to lazy memory reclaim and will be explained in the indices section.
10+
* [`DoublyListLazy`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyListLazy.html) and [`SinglyListLazy`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.SinglyListLazy.html)
11+
* *Lazy* suffix corresponds to lazy memory reclaim and will be explained in the indices section.
1112

1213
Some notable features are as follows.
1314

@@ -18,15 +19,15 @@ Link lists are self organizing to keep the nodes close to each other to benefit
1819
We observe in benchmarks that `DoublyList` is significantly faster than the standard linked list.
1920

2021
<details>
21-
<summary style="font-weight:bold;">A. Mutation At Ends</summary>
22+
<summary style="font-weight:bold;">A. Benchmark & Example: Mutation At Ends</summary>
2223

2324
In [doubly_mutation_ends.rs](https://github.com/orxfun/orx-linked-list/blob/main/benches/doubly_mutation_ends.rs) benchmark, we first push elements to a linked list until it reaches a particular length. Then, we call
2425
- `push_back`
2526
- `push_front`
2627
- `pop_back`
2728
- `pop_front`
2829

29-
in a random alternating order. We observe that `DoublyList` is around **40%** faster than `std::collections::LinkedList`.
30+
in a random alternating order. We observe that **DoublyList** is around **40% faster than std::collections::LinkedList**.
3031

3132
The following example demonstrates the simple usage of the list with mutation at its ends.
3233

@@ -52,31 +53,35 @@ assert_eq!(list.len(), 2);
5253
</details>
5354

5455
<details>
55-
<summary style="font-weight:bold;">B. Iteration</summary>
56+
<summary style="font-weight:bold;">B. Benchmark & Example: Iteration or Sequential Access</summary>
5657

5758
In [doubly_iter.rs](https://github.com/orxfun/orx-linked-list/blob/main/benches/doubly_iter.rs) benchmark, we create a linked list with insertions to the back and front of the list in a random order. Then, we `iter()` over the nodes and apply a map-reduce over the elements.
5859

59-
We observe that `DoublyList` iteration is around **25 times** faster than that with `std::collections::LinkedList`.
60+
We observe that **DoublyList** iteration is around **25 times faster than that with std::collections::LinkedList**.
6061

6162
The significance of improvement can further be increased by using `DoublyList::iter_x()` instead, which iterates over the elements in an arbitrary order. Unordered iteration is not suitable for all use cases. Most reductions or applying a mutation to each element are a couple of common examples. When the use case allows, unordered iteration further provides significant speed up.
6263

6364
</details>
6465

6566
## Iterations
6667

67-
Linked lists are all about traversal. And hence, the linked list, specifically the `DoublyList`, provides various useful ways to iterate over the data:
68-
* [`iter()`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter) iterates from front to back of the list
69-
* `iter().rev()` iterates from back to front
70-
* [`iter_from(idx: &DoublyIdx<T>)`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter_from) iterates forward starting from the node with the given index to the back
71-
* [`iter_backward_from(idx: &DoublyIdx<T>)`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter_backward_from) iterates forward starting from the node with the given index to the front
72-
* [`ring_iter(pivot_idx: &DoublyIdx<T>)`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.ring_iter) iterates forward starting from the pivot node with the given index until the node before the pivot node, linking back to the front and giving the list the **circular behavior**
73-
* [`iter_links()`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter_links) iterates over the links of the list
74-
* [`iter_x()`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyList.html#method.iter_x) iterates over elements in an arbitrary order, which is often faster when the order is not required
68+
Linked lists are all about traversal. Therefore, the linked lists defined in this crate, especially the **DoublyList**, provide various useful ways to iterate over the data:
69+
70+
| | |
71+
|------------|------------|
72+
| [`iter()`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter) | from front to back of the list |
73+
| `iter().rev()` | from back to front |
74+
| [`iter_from(idx: &DoublyIdx<T>)`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter_from) | forward starting from the node with the given index to the back |
75+
| [`iter_backward_from(idx: &DoublyIdx<T>)`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter_backward_from) | backward starting from the node with the given index to the front |
76+
| [`ring_iter(pivot_idx: &DoublyIdx<T>)`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.ring_iter) | forward starting from the pivot node with the given index until the node before the pivot node, linking back to the front and giving the list the **circular behavior** |
77+
| [`iter_links()`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.iter_links) | forward over the links, rather than nodes, from front to back |
78+
| [`iter_x()`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyList.html#method.iter_x) | over elements in an arbitrary order, which is often faster when the order is not required |
79+
|||
7580

7681
As typical, above-mentioned methods have the "_mut" suffixed versions for iterating over mutable references.
7782

7883
<details>
79-
<summary style="font-weight:bold;">Example</summary>
84+
<summary style="font-weight:bold;">Example: Iterations or Traversals</summary>
8085

8186
```rust
8287
use orx_linked_list::*;
@@ -113,14 +118,14 @@ assert_eq!(res, [3, 4, 5, 0, 1, 2]);
113118
```
114119
</details>
115120

116-
## Zero-Cost Append
121+
## Zero-Cost Append or Merge
117122

118123
Due to the feature of the [`Recursive`](https://docs.rs/orx-split-vec/3.8.0/orx_split_vec/struct.Recursive.html) growth strategy of the underlying SplitVec that allows merging vectors and the nature of linked lists, appending two lists is a constant time operation.
119124

120125
See [`append_front`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyList.html#method.append_front) and [`append_back`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyList.html#method.append_back).
121126

122127
<details>
123-
<summary style="font-weight:bold;">Example</summary>
128+
<summary style="font-weight:bold;">Example: Merging Linked Lists</summary>
124129

125130
```rust
126131
use orx_linked_list::*;
@@ -142,17 +147,17 @@ assert!(list.eq_to_iter_vals(['d', 'e', 'a', 'b', 'c']));
142147

143148
[`DoublyIdx<T>`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.DoublyIdx.html) and [`SinglyIdx<T>`](https://docs.rs/orx-linked-list/latest/orx_linked_list/type.SinglyIdx.html) are the node indices for doubly and singly linked lists, respectively.
144149

145-
A node index is analogous to `usize` for a slice (`&[T]`) in the following:
150+
A node index is analogous to **usize for a slice** (`&[T]`) in the following:
146151
* It provides constant time access to any element in the list.
147152

148-
It differs from `usize` for a slice due to the following:
153+
It differs from **usize for a slice** due to the following:
149154
* `usize` represents a position of the slice. Say we have the slice `['a', 'b', 'c']`. Currently, index **0** points to element `a`. However, if we swap the first and third elements, index **0** will now be pointing to `c` because the `usize` represents a position on the slice.
150155
* A node index represents the element it is created for. Say we now have a list `['a', 'b', 'c']` instead and `idx_a` is the index of the first element. It will always be pointing to this element no matter how many times we change its position, its value, etc.
151156

152157
Knowing the index of an element enables a large number of constant time operations. Below is a toy example to demonstrate how the index represents an element rather than a position, and illustrates some of the possible O(1) methods using it.
153158

154159
<details>
155-
<summary style="font-weight:bold;">Example</summary>
160+
<summary style="font-weight:bold;">Example: Constant Time Access Through Indices</summary>
156161

157162
```rust
158163
use orx_linked_list::*;
@@ -215,7 +220,7 @@ assert_eq!(list.idx_err(&idx), Some(NodeIdxError::RemovedNode));
215220
Each method adding an element to the list returns the index created for that particular node.
216221

217222
<details>
218-
<summary style="font-weight:bold;">push & insert</summary>
223+
<summary style="font-weight:bold;">Example: Individual node indices from push & insert</summary>
219224

220225
```rust
221226
use orx_linked_list::*;
@@ -237,7 +242,7 @@ assert_eq!(values, ['a', 'b', 'c', 'd']);
237242
Alternatively, we can collect all indices at once using the [`indices`](https://docs.rs/orx-linked-list/latest/orx_linked_list/trait.DoublyIterable.html#method.indices) method.
238243

239244
<details>
240-
<summary style="font-weight:bold;">indices()</summary>
245+
<summary style="font-weight:bold;">Example: All node indices from indices()</summary>
241246

242247
```rust
243248
use orx_linked_list::*;
@@ -277,7 +282,7 @@ Traditionally, linked lists provide constant time access to the ends of the list
277282
Indices enable slicing the list, which in turns behaves as a list reflecting its recursive nature.
278283

279284
<details>
280-
<summary style="font-weight:bold;">slice</summary>
285+
<summary style="font-weight:bold;">Example: Slicing linked lists</summary>
281286

282287
```rust
283288
use orx_linked_list::*;
@@ -315,7 +320,7 @@ assert!(list.eq_to_iter_vals([0, 12, 11, 42, 4, 5]));
315320
### Efficiency of Constant Time Mutations (Example)
316321

317322
How important are the additional O(1) methods?
318-
* In this talk [here](https://www.youtube.com/watch?v=YQs6IC-vgmo), Bjarne Stroustrup explains why we should avoid linked list. The talk nicely summarizes the trouble of achieving the appealing constant time mutation promise of linked list. Further, additional memory requirement due to the storage of links or pointers is mentioned.
323+
* In this talk [here](https://www.youtube.com/watch?v=YQs6IC-vgmo), Bjarne Stroustrup explains why we should avoid linked lists. The talk nicely summarizes the trouble of achieving the appealing constant time mutation promise of linked list. Further, additional memory requirement due to the storage of links or pointers is mentioned.
319324
* Likewise, there exists the following note in the documentation of the `std::collections::LinkedList`: "It is almost always better to use Vec or VecDeque because array-based containers are generally faster, more memory efficient, and make better use of CPU cache."
320325

321326
This crate aims to overcome the concerns with the following approach:
@@ -366,14 +371,7 @@ impl TourLinkedList {
366371

367372
Although clear from the worst time complexity of the implementations, [doubly_shuffling_around.rs](https://github.com/orxfun/orx-linked-list/blob/main/benches/doubly_shuffling_around.rs) benchmark demonstrates the dramatic difference. At each setting, we perform 10k `insert_after` moves with tours of different lengths. The following table summarizes the required time in microseconds for each setting.
368373

369-
| num_cities | DoublyList | Vec |
370-
|------------|------------|-----------|
371-
| 10 | 113 | 233 |
372-
| 100 | 152 | 834 |
373-
| 1,000 | 239 | 5,637 |
374-
| 10,000 | 885 | 84,890 |
375-
| 100,000 | 7,791 | 1,227,500 |
376-
||||
374+
<img src="https://raw.githubusercontent.com/orxfun/orx-linked-list/main/docs/img/tour-mutation-benchmark.PNG" alt="https://raw.githubusercontent.com/orxfun/orx-linked-list/main/docs/img/tour-mutation-benchmark.PNG" />
377375

378376
### Memory & Safety
379377

72.3 KB
Loading

src/iter/doubly_iter.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ where
3838
}
3939
}
4040

41-
impl<'a, T, P> DoubleEndedIterator for DoublyIter<'a, T, P>
41+
impl<T, P> DoubleEndedIterator for DoublyIter<'_, T, P>
4242
where
4343
P: PinnedVec<Node<Doubly<T>>>,
4444
{
@@ -50,9 +50,9 @@ where
5050
}
5151
}
5252

53-
impl<'a, T, P> FusedIterator for DoublyIter<'a, T, P> where P: PinnedVec<Node<Doubly<T>>> {}
53+
impl<T, P> FusedIterator for DoublyIter<'_, T, P> where P: PinnedVec<Node<Doubly<T>>> {}
5454

55-
impl<'a, T, P> Clone for DoublyIter<'a, T, P>
55+
impl<T, P> Clone for DoublyIter<'_, T, P>
5656
where
5757
P: PinnedVec<Node<Doubly<T>>>,
5858
{

src/iter/doubly_iter_mut.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ where
6868
}
6969
}
7070

71-
impl<'a, T, P> DoubleEndedIterator for DoublyIterMut<'a, T, P>
71+
impl<T, P> DoubleEndedIterator for DoublyIterMut<'_, T, P>
7272
where
7373
P: PinnedVec<Node<Doubly<T>>>,
7474
{
@@ -87,4 +87,4 @@ where
8787
}
8888
}
8989

90-
impl<'a, T, P> FusedIterator for DoublyIterMut<'a, T, P> where P: PinnedVec<Node<Doubly<T>>> {}
90+
impl<T, P> FusedIterator for DoublyIterMut<'_, T, P> where P: PinnedVec<Node<Doubly<T>>> {}

src/iter/doubly_iter_mut_chain.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,4 @@ where
5959
}
6060
}
6161

62-
impl<'a, T, P> FusedIterator for DoublyIterMutChain<'a, T, P> where P: PinnedVec<Node<Doubly<T>>> {}
62+
impl<T, P> FusedIterator for DoublyIterMutChain<'_, T, P> where P: PinnedVec<Node<Doubly<T>>> {}

src/iter/doubly_iter_ptr.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ where
3737
}
3838
}
3939

40-
impl<'a, T, P> Iterator for DoublyIterPtr<'a, T, P>
40+
impl<T, P> Iterator for DoublyIterPtr<'_, T, P>
4141
where
4242
P: PinnedVec<Node<Doubly<T>>>,
4343
{
@@ -59,7 +59,7 @@ where
5959
}
6060
}
6161

62-
impl<'a, T, P> DoubleEndedIterator for DoublyIterPtr<'a, T, P>
62+
impl<T, P> DoubleEndedIterator for DoublyIterPtr<'_, T, P>
6363
where
6464
P: PinnedVec<Node<Doubly<T>>>,
6565
{
@@ -80,9 +80,9 @@ where
8080
}
8181
}
8282

83-
impl<'a, T, P> FusedIterator for DoublyIterPtr<'a, T, P> where P: PinnedVec<Node<Doubly<T>>> {}
83+
impl<T, P> FusedIterator for DoublyIterPtr<'_, T, P> where P: PinnedVec<Node<Doubly<T>>> {}
8484

85-
impl<'a, T, P> Clone for DoublyIterPtr<'a, T, P>
85+
impl<T, P> Clone for DoublyIterPtr<'_, T, P>
8686
where
8787
P: PinnedVec<Node<Doubly<T>>>,
8888
{

src/iter/doubly_link_iter.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ where
4040
}
4141
}
4242

43-
impl<'a, T, P> FusedIterator for DoublyLinkIter<'a, T, P> where P: PinnedVec<Node<Doubly<T>>> {}
43+
impl<T, P> FusedIterator for DoublyLinkIter<'_, T, P> where P: PinnedVec<Node<Doubly<T>>> {}
4444

45-
impl<'a, T, P> Clone for DoublyLinkIter<'a, T, P>
45+
impl<T, P> Clone for DoublyLinkIter<'_, T, P>
4646
where
4747
P: PinnedVec<Node<Doubly<T>>>,
4848
{

src/iter/doubly_link_iter_ptr.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ where
3939
}
4040
}
4141

42-
impl<'a, T, P> Iterator for DoublyLinkIterPtr<'a, T, P>
42+
impl<T, P> Iterator for DoublyLinkIterPtr<'_, T, P>
4343
where
4444
P: PinnedVec<Node<Doubly<T>>>,
4545
{
@@ -66,9 +66,9 @@ where
6666
}
6767
}
6868

69-
impl<'a, T, P> FusedIterator for DoublyLinkIterPtr<'a, T, P> where P: PinnedVec<Node<Doubly<T>>> {}
69+
impl<T, P> FusedIterator for DoublyLinkIterPtr<'_, T, P> where P: PinnedVec<Node<Doubly<T>>> {}
7070

71-
impl<'a, T, P> Clone for DoublyLinkIterPtr<'a, T, P>
71+
impl<T, P> Clone for DoublyLinkIterPtr<'_, T, P>
7272
where
7373
P: PinnedVec<Node<Doubly<T>>>,
7474
{

src/iter/singly_iter.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ where
3434
}
3535
}
3636

37-
impl<'a, T, P> FusedIterator for SinglyIter<'a, T, P> where P: PinnedVec<Node<Singly<T>>> {}
37+
impl<T, P> FusedIterator for SinglyIter<'_, T, P> where P: PinnedVec<Node<Singly<T>>> {}
3838

39-
impl<'a, T, P> Clone for SinglyIter<'a, T, P>
39+
impl<T, P> Clone for SinglyIter<'_, T, P>
4040
where
4141
P: PinnedVec<Node<Singly<T>>>,
4242
{

0 commit comments

Comments
 (0)