Quick Start Guide
Relevant source files
This guide covers the essential steps to start using the linked_list_r4l crate for basic linked list operations. It demonstrates node creation using the def_node! macro and common list operations like insertion, removal, and iteration. For detailed architecture information, see Architecture Overview. For comprehensive API documentation, see API Reference.
Prerequisites and Setup
Add the crate to your Cargo.toml:
[dependencies]
linked_list_r4l = "0.1"
The library provides two main approaches for creating list nodes:
- Recommended: Using the
def_node!macro for automatic code generation - Manual: Implementing the
GetLinkstrait manually
Creating Nodes with def_node! Macro
The def_node! macro is the primary way to create node types. It automatically generates the required structure and trait implementations.
Basic Node Definition
use linked_list_r4l::{def_node, List};
def_node! {
/// A simple node containing a usize value
struct NumberNode(usize);
/// A public node with generic type
pub struct GenericNode<T>(T);
}
The macro generates a struct with two fields: inner containing your data and links for list management. It also implements the GetLinks trait and provides helper methods.
Sources: src/lib.rs(L109 - L178)
Node Creation and Basic Operations
// Create nodes
let node1 = Box::new(NumberNode::new(42));
let node2 = Box::new(NumberNode::new(100));
// Create list
let mut list = List::<Box<NumberNode>>::new();
// Add nodes
list.push_back(node1);
list.push_back(node2);
// Access data
for node in list.iter() {
println!("Value: {}", *node.inner());
}
// Remove nodes
let first = list.pop_front().unwrap();
assert_eq!(first.into_inner(), 42);
Sources: src/lib.rs(L136 - L165)
Generated Node Structure
When you use def_node!, the following structure is generated:
flowchart TD
UserMacro["def_node! { struct NumberNode(usize); }"]
GeneratedStruct["Generated Struct NumberNode• inner: usize• links: Links<Self>"]
GeneratedImpl["Generated Implementations• GetLinks trait• new() constructor• inner() accessor• into_inner() converter• Deref to inner type"]
UserCode["User Codelet node = NumberNode::new(42)"]
ListAPI["List APIlist.push_back(Box::new(node))"]
GeneratedImpl --> UserCode
GeneratedStruct --> GeneratedImpl
UserCode --> ListAPI
UserMacro --> GeneratedStruct
Sources: src/lib.rs(L12 - L58) src/lib.rs(L60 - L107)
List Operations Flow
The following diagram shows how common list operations map to code entities:
flowchart TD
subgraph subGraph2["Core Types"]
ListStruct["List<G>"]
LinksStruct["Links<T>"]
GetLinksTrait["GetLinks trait"]
end
subgraph subGraph1["Node Management"]
BoxWrapper["Box<NumberNode>"]
NodeData["NumberNode.inner"]
NodeLinks["NumberNode.links"]
end
subgraph subGraph0["User Operations"]
CreateList["List::new()"]
AddNode["list.push_back(node)"]
RemoveNode["list.pop_front()"]
IterateList["list.iter()"]
end
AddNode --> BoxWrapper
BoxWrapper --> NodeData
BoxWrapper --> NodeLinks
CreateList --> ListStruct
IterateList --> GetLinksTrait
LinksStruct --> GetLinksTrait
ListStruct --> LinksStruct
NodeLinks --> LinksStruct
RemoveNode --> BoxWrapper
Sources: src/lib.rs(L6) src/lib.rs(L7) src/lib.rs(L19 - L26)
Complete Example
Here's a comprehensive example showing typical usage patterns:
use linked_list_r4l::{def_node, List};
def_node! {
/// Task node for a simple scheduler
pub struct TaskNode(String);
/// Priority task with generic data
pub struct PriorityTask<T>(T);
}
fn main() {
// Create task list
let mut task_queue = List::<Box<TaskNode>>::new();
// Add tasks
task_queue.push_back(Box::new(TaskNode::new("initialize".to_string())));
task_queue.push_back(Box::new(TaskNode::new("process".to_string())));
task_queue.push_back(Box::new(TaskNode::new("cleanup".to_string())));
// Process tasks
while let Some(task) = task_queue.pop_front() {
println!("Processing: {}", task.inner());
// Task is automatically dropped here
}
// Generic example
let mut numbers = List::<Box<PriorityTask<i32>>>::new();
numbers.push_back(Box::new(PriorityTask::new(1)));
numbers.push_back(Box::new(PriorityTask::new(2)));
// Iterate without removing
for (index, task) in numbers.iter().enumerate() {
println!("Task {}: {}", index, task.inner());
}
}
Sources: src/lib.rs(L125 - L165)
Memory Management
The library uses Box<T> for heap allocation by default, but supports other ownership models:
| Wrapper Type | Use Case | Example |
|---|---|---|
| Box | Single ownership, heap allocated | List::<Box |
| Arc | Shared ownership, reference counted | List::<Arc |
| &Node | Borrowed references | List::<&MyNode>::new() |
Sources: src/lib.rs(L6)
Manual Node Implementation (Alternative)
For advanced use cases, you can implement the GetLinks trait manually instead of using the macro:
#![allow(unused)] fn main() { use linked_list_r4l::{GetLinks, Links, List}; pub struct CustomNode { pub data: String, links: Links<Self>, } impl GetLinks for CustomNode { type EntryType = Self; fn get_links(t: &Self) -> &Links<Self> { &t.links } } impl CustomNode { fn new(data: String) -> Self { Self { data, links: Links::new(), } } } }
Sources: README.md(L15 - L44)
Next Steps
- For understanding the library's architecture, see Architecture Overview
- For advanced ownership patterns, see Advanced API
- For thread safety considerations, see Thread Safety
- For contributing to the project, see Development Guide