3 releases
Uses new Rust 2024
new 0.0.5 | Mar 16, 2025 |
---|---|
0.0.4 | Mar 15, 2025 |
0.0.3 | Mar 15, 2025 |
0.0.2 |
|
#483 in Data structures
118 downloads per month
4MB
529 lines
Contains (WOFF font, 400KB) NanumBarunGothic-13b3dcba.ttf.woff2, (WOFF font, 135KB) FiraSans-Medium-e1aa3f0a.woff2, (WOFF font, 130KB) FiraSans-Regular-0fe48ade.woff2, (WOFF font, 82KB) SourceSerif4-Bold-6d4fd4c0.ttf.woff2, (WOFF font, 77KB) SourceSerif4-Regular-6b053e98.ttf.woff2, (WOFF font, 45KB) SourceCodePro-It-fc8b9304.ttf.woff2 and 3 more.
8-bit Ferris by YakoYakoYokuYoku & ryanobeirne
Canopy is a small tree-based data structure implemented in Rust. It provides a way to model hierarchical relationships with two types of nodes: Node::Parent
and Node::Leaf
. The structure is defined as follows:
enum Node<T> {
Leaf {
prev: Option<PrevNodeRef<T>>,
value: T,
},
Parent {
value: T,
prev: Option<PrevNodeRef<T>>,
next: Vec<NodeRef<T>>,
},
}
Node::Parent
nodes hold references to their children and optionally to their parents, along with their value.Node::Leaf
nodes store just a value and do not have any children, making them terminal points in the tree structure. However, leaf nodes may also be able to be upgraded toNode::Parents
allowing them to have children.- std library feature where
PrevNodeRef
, isWeak<RefCell<T>>
whileno_std
usesrclite::Rc<RefCell<T>>
.
- std library feature where
Canopy uses Rust’s pattern within to enable shared mutability and ownership, which makes it well-suited for managing dynamic, tree-like data.
Quick Tour
graph TD;
root-->child;
root-->child2;
child2-->grand_child1;
child2-->grand_child2;
To quickly get acquainted with the interface, let's create a simple graph structure like the one above. We'll also demonstrate some basic operations available in libcanopy
.
Insert
To build our structure, we start by instantiating the parent node using Node::parent()
. This function returns a reference-counted Node::Parent
.
To add children, we pass a reference to the parent node and the value we want to insert. This rule applies recursively to all descendants.
use libcanopy::{Node, NodeRef, error::NodeError};
fn main() -> Result<(), NodeError> {
let root: NodeRef<u8> = Node::parent(1);
// `child` is now a Node::Leaf that points to `root`.
let child: NodeRef<u8> = Node::insert(&root, 2)?;
// `child2` is now a Node::Leaf that points to `root`.
let child2: NodeRef<u8> = Node::insert(&root, 3)?;
// `child2` is upgraded to Node::Parent that points to `root`,
// and has `grand_child1` as a child.
let grand_child1: NodeRef<u8> = Node::insert(&child2, 4)?;
// `grand_child2` is added as another child of `child2`.
let grand_child2: NodeRef<u8> = Node::insert(&child2, 5)?;
Ok(())
}
Pop
The pop
operation removes a child node from its parent. If all children are removed, the parent node is downgraded back to a Node::Leaf
.
fn main() -> Result<(), NodeError> {
// Assume nodes are created as shown in the previous example.
Node::pop(&child2, &grand_child1)?;
Node::pop(&child2, &grand_child2)?;
// Both children of `child2` have been removed,
// so `child2` downgrades back to a `Node::Leaf`.
Ok(())
}
Iterate
We can iterate over the nodes using Node::iter()
. This allows us to traverse the tree structure.
use libcanopy::{Node, NodeRef, NodeIter};
fn main() -> Result<(), NodeError> {
let root: NodeRef<u8> = Node::parent(1);
let child: NodeRef<u8> = Node::insert(&root, 2)?;
let child2: NodeRef<u8> = Node::insert(&root, 3)?;
let grand_child1: NodeRef<u8> = Node::insert(&child2, 4)?;
let grand_child2: NodeRef<u8> = Node::insert(&child2, 5)?;
let mut nodes: NodeIter<u8> = Node::iter(root.clone());
while let Some(node) = nodes.next() {
// order printed out: 1, 2, 3, 4, 5
println!("{}", node.borrow().value());
}
Ok(())
}
Features
- Tree-based structure with mutable and shared ownership via
Rc<RefCell<T>>
, andWeak<RefCell<T>>
. - Ability to model both parent-child relationships.
- Safety-focused code development, on trying to adhering to the "Power of 10" rules for safety-critical systems.
- Iter though using a BFS data-type
NodeIter<T>
- Supports
#[no_std]
Installation
To use Canopy in your project, add it to your Cargo.toml
:
[dependencies]
# Git version
# libcanopy = { git = "https://github.com/LVivona/canopy", branch = "main" }
# Cargo.io version
libcanopy = { version = "*" }
Closing Remarks
This project is also a personal experiment to apply best practices in developing safety-critical code. By adhering to the "Power of 10" rules for writing safe and reliable systems, the goal is to create robust, memory-safe code while exploring the depths of Rust’s safety features.