📖 Scenario: You are managing a simple list of tasks to do. You want to add new tasks at the end of the list so you can complete them in order.
🎯 Goal: Build a Rust program that creates a linked list of tasks and adds a new task at the end (tail) of the list.
📋 What You'll Learn
Create a linked list node struct called Node with a task field and a next pointer
Create a linked list struct called TaskList with a head pointer
Add a method append to TaskList that inserts a new task at the end
Print the tasks in order after insertion
💡 Why This Matters
🌍 Real World
Linked lists are useful for managing ordered collections where you add or remove items dynamically, like task lists, playlists, or undo histories.
💼 Career
Understanding linked lists and how to insert at the tail is fundamental for software developers working with data structures, memory management, and performance optimization.
Progress0 / 4 steps
1
Create the linked list node and list structs
Create a struct called Node with a task field of type String and a next field of type Option>. Also create a struct called TaskList with a head field of type Option>.
DSA Rust
Hint
Use Option> for the next pointer to allow for empty or next nodes.
2
Add an append method to insert at the end
Add an impl block for TaskList and write a method called append that takes &mut self and a task: String. This method should insert a new Node with the given task at the end of the list.
DSA Rust
Hint
Use as_mut() to get mutable references and loop until next is None to find the tail.
3
Create a method to print all tasks
Add a method called print_tasks to TaskList that prints each task in order, separated by -> , ending with None.
DSA Rust
Hint
Use as_ref() to get references and loop through nodes until None.
4
Add tasks and print the list
In the main function, create a mutable TaskList called tasks. Append the tasks "Buy groceries", "Clean room", and "Pay bills" in that order. Then call tasks.print_tasks() to display the list.
DSA Rust
Hint
Remember to create tasks as mutable and call append with to_string() on each task.
Practice
(1/5)
1. What does the "Insert at End (Tail Insert)" operation do in a linked list?
easy
A. Deletes the last node of the list
B. Removes the first node of the list
C. Adds a new node at the end of the list
D. Adds a new node at the beginning of the list
Solution
Step 1: Understand the operation name
"Insert at End" means adding a new element at the tail (last position) of the list.
Step 2: Compare with other operations
Adding at the beginning is different; removing nodes is deletion, not insertion.
Final Answer:
Adds a new node at the end of the list -> Option C
Quick Check:
Insert at End = Add node at tail [OK]
Hint: Tail insert always adds node at the list's end [OK]
Common Mistakes:
Confusing insert at end with insert at beginning
Thinking it removes nodes instead of adding
Mixing insertion with deletion operations
2. Which of the following Rust code snippets correctly inserts a new node with value 10 at the end of a singly linked list?
easy
A. let mut current = &mut head;
while current.is_some() {
current = &mut current.as_mut().unwrap().next;
}
*current = Some(Box::new(Node { value: 10, next: None }));
B. head = Some(Box::new(Node { value: 10, next: head }));
C. head = None;
D. let mut current = &head;
while current.is_some() {
current = ¤t.unwrap().next;
}
current = Some(Box::new(Node { value: 10, next: None }));
Solution
Step 1: Analyze code for tail insertion
let mut current = &mut head;
while current.is_some() {
current = &mut current.as_mut().unwrap().next;
}
*current = Some(Box::new(Node { value: 10, next: None })); traverses the list using mutable references until it finds None, then inserts the new node.
Step 2: Check other options for correctness
head = Some(Box::new(Node { value: 10, next: head })); inserts at the head, not tail. head = None; clears the list. let mut current = &head;
while current.is_some() {
current = ¤t.unwrap().next;
}
current = Some(Box::new(Node { value: 10, next: None })); uses immutable references, causing compilation errors on unwrap() and rebinding, and doesn't modify the list.
Final Answer:
The mutable reference traversal code correctly inserts at the end -> Option A
Quick Check:
Mutable traversal + insert at None = tail insert [OK]
Hint: Use mutable references to find None and insert [OK]
Common Mistakes:
Using immutable references when mutability is needed
Inserting at head instead of tail
Assigning to immutable references causing errors
3. What will be the output of the following Rust code that inserts values 1, 2, 3 at the end of an empty linked list and then prints it?
struct Node {
value: i32,
next: Option,
}
fn main() {
let mut head: Option = None;
for val in [1, 2, 3] {
let mut current = &mut head;
while current.is_some() {
current = &mut current.as_mut().unwrap().next;
}
*current = Some(Box::new(Node { value: val, next: None }));
}
let mut current = &head;
while let Some(node) = current {
print!("{} -> ", node.value);
current = &node.next;
}
print!("null\n");
}
medium
A. 3 -> 2 -> 1 -> null
B. 1 -> 2 -> 3 -> null
C. null
D. Compilation error
Solution
Step 1: Trace insertion loop
The insertion loop correctly adds 1, then 2, then 3 at the tail, building 1 -> 2 -> 3 -> None.
Step 2: Trace print loop
The print loop uses `let mut current = &head;` and `while let Some(node) = current` which causes a type mismatch because `current` is a reference to an Option, not an Option itself. To fix this, the code should use `current = current.as_ref().map(|node| &node.next)` or `while let Some(node) = current.as_ref()` to correctly traverse the list immutably. However, as given, the code will not compile due to this mismatch.
Step 3: Correction
Assuming the print loop is fixed to use `while let Some(node) = current.as_ref()`, the output will be `1 -> 2 -> 3 -> null`.