📖 Scenario: You are building a simple program to manage a list of tasks. Each task should be connected to the previous and next task so you can move forward and backward easily.
🎯 Goal: Create a doubly linked list in Rust using a Vec of Nodes with index-based pointers with three tasks: "Task1", "Task2", and "Task3". Initialize the list so each task points to the correct previous and next tasks using indices.
📋 What You'll Learn
Define a struct called Node with fields value, prev, and next of type Option<usize>
Create a mutable Vec<Node> called nodes with three nodes having values "Task1", "Task2", and "Task3"
Link the nodes so nodes[0].next = Some(1), nodes[1].prev = Some(0), nodes[1].next = Some(2), nodes[2].prev = Some(1)
Let head = 0
Print the list forward starting from head showing the values connected by -> and ending with null
💡 Why This Matters
🌍 Real World
Doubly linked lists are used in applications like undo/redo features, navigation history, and playlist management where moving forward and backward is needed.
💼 Career
Understanding doubly linked lists helps in roles involving system programming, game development, and building efficient data structures.
Progress0 / 4 steps
1
Define the Node struct
Define a struct called Node with a value field of type String, and two optional index fields called prev and next of type Option<usize>.
DSA Rust
Hint
Use Option<usize> for prev and next to store indices into a Vec of Nodes.
2
Create mutable Vec of nodes and head
Inside main(), create a mutable variable called nodes of type Vec<Node> initialized with three nodes having values "Task1", "Task2", and "Task3" respectively, with prev and next set to None. Create head set to 0.
DSA Rust
Hint
Use vec![ Node { .. }, .. ] to create the list of nodes on the stack/heap safely.
3
Link the nodes to form a doubly linked list
Link the nodes using indices: set nodes[0].next = Some(1), nodes[1].prev = Some(0) and nodes[1].next = Some(2), nodes[2].prev = Some(1).
DSA Rust
Hint
Access nodes by index like an array and set prev/next to the index of the neighboring node.
4
Print the doubly linked list forward
Create a variable current starting at Some(head). Use a while let loop to print the value of each node (using nodes[current.unwrap()].value or pattern matching) followed by -> . Move current to the next index each time. After the loop, print null.
DSA Rust
Hint
Use a while let Some(idx) = current loop and update current = nodes[idx].next each time.
Practice
(1/5)
1. What is the main purpose of a doubly linked list in data structures?
easy
A. To allow traversal in both forward and backward directions
B. To store data in a fixed-size array
C. To sort elements automatically
D. To prevent duplicate values
Solution
Step 1: Understand doubly linked list structure
A doubly linked list has nodes with pointers to both previous and next nodes.
Step 2: Identify traversal capability
This structure allows moving forward and backward through the list easily.
Final Answer:
To allow traversal in both forward and backward directions -> Option A
Quick Check:
Doubly linked list traversal = forward and backward [OK]
Hint: Remember: double links mean two-way movement [OK]
Common Mistakes:
Confusing doubly linked list with arrays
Thinking it sorts data automatically
Assuming it prevents duplicates
2. Which Rust code snippet correctly defines a node struct for a doubly linked list?
Rust uses Option for next to allow nullable ownership and *mut Node for raw pointer prev.
Step 2: Check each option for correctness
struct Node { data: i32, next: Option, prev: Option<*mut Node> } uses Option for next and Option<*mut Node> for prev, which is correct for doubly linked list nodes in Rust.
Rust doubly linked list node = Option<Box> + raw pointer [OK]
Hint: Use Option<Box> for next and raw pointer for prev in Rust [OK]
Common Mistakes:
Using direct Node type causing infinite size error
Using Box<Node> for prev causing ownership issues
Not using Option to allow null pointers
3. What will be the output after running this Rust code that creates a doubly linked list with nodes 1 and 2?
struct Node {
data: i32,
next: Option,
prev: Option<*mut Node>,
}
fn main() {
let mut first = Box::new(Node { data: 1, next: None, prev: None });
let mut second = Box::new(Node { data: 2, next: None, prev: None });
first.next = Some(second);
// Set prev pointer
if let Some(ref mut next_node) = first.next {
next_node.prev = Some(&mut *first as *mut Node);
}
println!("{} -> {} -> null", first.data, first.next.as_ref().unwrap().data);
}
medium
A. 1 -> 2 -> null
B. 2 -> 1 -> null
C. Compilation error due to ownership
D. Runtime panic due to null pointer
Solution
Step 1: Analyze node creation and linking
Two nodes created: first with data 1, second with data 2. first.next points to second, second.prev points back to first.
Step 2: Understand ownership rules in Rust
Assigning first.next = Some(second) moves ownership of second into first.next. However, second is moved and cannot be used after. Also, setting next_node.prev = Some(&mut *first as *mut Node) involves mutable borrow of first while first is already borrowed mutably, causing ownership conflict.
Step 3: Compilation error due to ownership and borrowing rules
The code will not compile due to ownership and borrowing errors.
A. Cannot assign reference to raw pointer without unsafe block
B. Missing lifetime annotation for prev pointer
C. Cannot borrow `node` as mutable more than once
D. No error, code runs fine
Solution
Step 1: Understand raw pointer assignment in Rust
Assigning a reference (&mut node) to a raw pointer field (prev) requires an unsafe block because raw pointer dereferencing and assignment are unsafe operations.
Step 2: Identify missing unsafe block
The code attempts to assign &mut node to prev without an unsafe block, causing a compilation error.
Final Answer:
Cannot assign reference to raw pointer without unsafe block -> Option A
Quick Check:
Raw pointer assignments require unsafe block in Rust [OK]
Hint: Use unsafe block when assigning raw pointers [OK]
Common Mistakes:
Ignoring unsafe requirement for raw pointers
Thinking mutable borrow error occurs here
Assuming code compiles without error
5. You want to create a doubly linked list in Rust that supports inserting a new node at the front. Which approach correctly initializes the new node and updates pointers?
struct Node {
data: i32,
next: Option,
prev: Option<*mut Node>,
}
fn insert_front(head: &mut Option, value: i32) {
// Fill in the missing code here
}
hard
A. Create new node with next = None, prev = head; set head to new node without changing old head
B. Create new node with next = None, prev = None; set head to new node without linking
C. Create new node with next = head, prev = head; set head to new node
D. Create new node with next = head.take(), prev = None; if old head exists, set its prev to new node pointer; then set head to new node
Solution
Step 1: Create new node with next pointing to old head
Use head.take() to move old head out safely and assign to new node's next; prev is None because new node is front.
Step 2: Update old head's prev pointer if it exists
If old head exists, set its prev pointer to new node's raw pointer to maintain backward link.
Step 3: Update head to point to new node
Assign new node to head to complete insertion at front.
Final Answer:
Create new node with next = head.take(), prev = None; if old head exists, set its prev to new node pointer; then set head to new node -> Option D
Quick Check:
Insert front updates next, prev, and head correctly [OK]
Hint: Use head.take() and update old head's prev pointer [OK]
Common Mistakes:
Not updating old head's prev pointer
Setting prev pointer incorrectly
Overwriting head without moving old head
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.