|
1 | 1 | # Doubly Linked List
|
| 2 | +This a very common data struct that you encounter as a developer with a lot of use cases. It is also relatively easy to implement. I implement it in rust for an [exercism](https://exercism.io/) exercise. This article provides a top level walkthrough of the exercise without any implementation details that will spoil the solution, but I do provide a solution at the end. |
2 | 3 |
|
3 |
| -Write a doubly linked list using unsafe Rust, including an iterator over the list |
4 |
| -and a cursor for efficient mutation. |
5 |
| - |
6 |
| -The doubly linked list is a fundamental data structure in computer science, |
7 |
| -often used in the implementation of other data structures. They're |
8 |
| -pervasive in functional programming languages, such as Clojure, Erlang, |
9 |
| -or Haskell, but far less common in imperative languages such as Ruby or |
10 |
| -Python. |
11 |
| - |
12 |
| -Each node in a doubly linked list contains data and pointers to the next |
13 |
| -and previous node, if they exist. |
14 |
| - |
15 |
| -New nodes can be efficiently added at any point in the list, if one already has |
16 |
| -a reference to the position. Likewise, all elements |
17 |
| -from another list can be inserted at any point in constant time. |
18 |
| - |
19 |
| -In Rust, linked lists are very rarely used, but occasionally they trip up |
20 |
| -newcomers, when they try implementing one. Often, they find it unexpectedly |
21 |
| -difficult to work with the yet unfamiliar borrow checker. |
22 |
| - |
23 |
| -# Solution |
24 |
| - |
25 |
| -Most of the problem is defining the proper struct to represent each node. For me it looks like this |
| 4 | +# Explanation |
| 5 | +Most of the problem is defining the proper struct to reference and store your data. The basic block of a `linked list` is a **Node** that stores the data and 2 pointers (previous and next). For me it looks like this |
26 | 6 |
|
27 | 7 | ```rust
|
28 | 8 | pub struct Node<T> {
|
29 | 9 | pub value: T,
|
30 | 10 | pub previous: *mut Node<T>,
|
31 | 11 | pub next: *mut Node<T>,
|
32 | 12 | }
|
33 |
| -``` |
| 13 | +``` |
| 14 | + |
| 15 | +Now the `doubly linked list` needs to store a referense to the first and also to the last Node. We access the nodes in the middle by ***walking*** the list from top to bottom and reverse. Here is the struct: |
| 16 | + |
| 17 | +```rust |
| 18 | +pub struct LinkedList<T> { |
| 19 | + pub head: *mut Node<T>, |
| 20 | + pub tail: *mut Node<T>, |
| 21 | + size: usize, |
| 22 | +} |
| 23 | + |
| 24 | +pub struct Cursor<'a, T> { |
| 25 | + list: &'a mut LinkedList<T>, |
| 26 | + current: *mut Node<T>, |
| 27 | +} |
| 28 | +``` |
| 29 | + |
| 30 | +A **Cursor**, as the name suggests, points to specific element in the list and allows us to edit the list, add/remove nodes. It is also important to 'point out' that a cursor can change the head or the tail of the list and that's why he needs a mutable reference to it. |
| 31 | + |
| 32 | +# Unsafe |
| 33 | +You can clearly see in the code above that I store pointer inside my structs. This is a key difference of this exercise. You **have** to use unsafe in order to dereference this pointer and that is ok. The point of this exercise is to use unsafe code in order to provide a safe API for the users. It is your job to ensure that your code never fails unexpectedly. |
| 34 | + |
| 35 | +# More |
| 36 | +Now you are ready to solve this exercise on your own. If you need any more help, I will provide links to resources that can help you and of course my own solution. I suggest you try solving it first. |
| 37 | +- [Geeksforgeeks](https://www.geeksforgeeks.org/doubly-linked-list/) |
| 38 | +- [Wikipedia](https://en.wikipedia.org/wiki/Doubly_linked_list) |
| 39 | +- [My Solution](https://github.com/Dimkar3000/exercism/tree/master/rust/doubly-linked-list) |
0 commit comments