#node-tree #tree-structure #tree-node #tree #vec #data-structure

tree_by_path

A tree data structure featuring nodes addressable using a &Vec<usize> path and avoiding recursion and run-time borrow checking

4 stable releases

1.0.3 Nov 22, 2023
1.0.0 Nov 21, 2023

#485 in Data structures

Download history 5/week @ 2024-02-19 14/week @ 2024-02-26 4/week @ 2024-03-04 13/week @ 2024-03-11 22/week @ 2024-04-01 100/week @ 2024-04-15

122 downloads per month
Used in masterpg

MIT/Apache

110KB
1.5K SLoC

tree_by_path

by Koen Bekx

Usage

In order to use this crate, add it to your project's Cargo.toml file using the command

cargo add tree_by_path

and add the below statement in your code :

use tree_by_path::{Node, PathError, TraverseAction};

Trees are Nodes

pub struct Node<C> {
    pub cargo: C,
    pub children: Vec<Node<C>>,
}

tree_by_path trees consist of a Node<C> root node that may or may not have Node<C> children, who in turn may or may not have other Node<C> children etc.

Every node carries a useful cargo of generic type C ("cargo"). (Nullability of the cargo can be obtained by instantiating a tree having Node<Option<YourType>> nodes.)

Child nodes are directly owned by parent nodes as elements of their children vector : Vec<Node<C>>.

Child nodes hold no reference to their parent. This parent, however, can always be retrieved by removing the last element of the indexes array the node has been addressed with - see below.

This design has been chosen so as to avoid the use of RefCell or other inner mutability mechanisms, which defer borrow checking from compilation to runtime.

Address paths

Except for the root node, all nodes in the tree are an n-th element in their parent node's children collection.

This means that every node can be addressed using a Vec<usize> series of indexes :

  • the root node's address is an empty series of indexes : [];
  • any direct child node under the root node has one index in its address, starting with [0];
  • other nodes have a longer address, e.g.:
 []
  |
 [0]-----------------[1]-----------------[2]
  |                   |
 [0,0]---[0,1]       [1,0]---[1,1]
          |                   |
         [0,1,0]             [1,1,0]---[1,1,1]---[1,1,2]

These addresses are the paths meant in the crate's name.

Use of these addresses, which are &Vec<usize> objects accepted and returned by nearly all methods of struct Node<C>, allow code using this crate to hold only one mutable reference to a root node, without needing any other references, thus avoiding the need of holding on to multiple mutable references of nodes of the same tree, which would be prohibited by Rust's borrow checker.

So, instead of references to nodes, instances of Vec<usize> paths can be kept in variables.

And even if these path addresses can become obsolete after insertion or removal of nodes, specific nodes can be retrieved by lookups on the nodes' cargo using the traverse* methods if needed - see the section Node identifiers in the crate's documentation.

Cyclic parent-child node ownership

No way has been found to force a node to have one of its parent nodes as a child node in its children vector, and that's for the better.

Cloning

Node<C> is clonable if type C is clonable.

Examples

For usage examples, please see the crate's documentation or even the unit tests in src/lib.rs.

No runtime deps