Warning
This post was published 32 days ago. The information described in this article may have changed.
I'm writing a general, n-ary hierarchical (tree) structure and I'm looking for some design advice. I currently have both a CursorMut
and Position
struct for the implementation. The Position
struct is really just a safe handle to the underlying Node<T>
in the vein of a type alias like type Link<T> = Option<NonNull<Node<T>>>;
, except I can keep it private(?). I'm wondering if this approach is redundant/provides too much indirection, or if this is the correct approach for the safe/generic API. Either way, I'm curious to hear your opinions about what kinds of operations each of these member structs should have, if any.
The private Node
:
struct Node<T> {
parent: Option<Position<T>>,
children: Vec<Position<T>>, // Always exists for a Node, even if empty
data: Option<T>,
}
A sketch of the (mostly implemented/tested) public API:
impl<T> GenTree<T>
pub fn new() -> GenTree<T>
pub fn root(&self) -> Position<T>
pub fn cursor_mut(&mut self) -> CursorMut<'_, T>
// Unimplemented
pub fn cursor_from(&mut self, position: &Position<T>) -> CursorMut<'_, T>
// Unimplemented
pub fn depth(&self, node: &Position<T>) -> usize
// Unimplemented
pub fn height(&self, node: &Position<T>) -> usize
impl<T> Position<T>
pub fn get_data(&self) -> Option<&T>
impl<'a, T> CursorMut<'a, T>
pub fn is_root(&self) -> bool
pub fn num_children(&self) -> usize
pub fn get_data(&self) -> Option<&T>
pub fn add_child(&mut self, data: T)
pub fn delete(&mut self) -> Option<T>
pub fn current(&self) -> &Position<T>
pub fn jump(&mut self, new: &Position<T>)
pub fn ascend(&mut self) -> Result<(), String>
pub fn children(&self) -> Vec<Position<T>>
This allows me to do things like:
for position in cursor.children() {
if position.get_data().unwrap().title == "Marine".to_string() {
cursor.jump(&position);
deleted = cursor.delete().unwrap();
}
}
let mut tree: GenTree<Heading> = GenTree::<Heading>::new();
let mut cursor = tree.cursor_mut(); // Sets cursor to tree.root
// Constructs tree from Vec<T>
for node in data {
let data_level = node.level;
// Case 1: Adds a child to the current parent
if data_level == cur_level + 1 {
cursor.add_child(node);
cur_level += 1;
}
// Case 2: Adds a child with multi-generational skips
else if data_level > cur_level {
let diff = data_level - cur_level;
for _ in 1..diff {
let empty = Heading::new("[]".to_string(), 0);
cursor.add_child(empty);
cur_level += 1;
}
cursor.add_child(node);
cur_level += 1;
}
// Case 3: Adds sibling to current parent
else if data_level == cur_level {
cursor.ascend().ok();
cursor.add_child(node);
}
// Case 4: Adds a child to the appropriate ancestor,
// ensuring proper generational skips
else {
let diff = cur_level - data_level;
for _ in 0..=diff {
cursor.ascend().ok();
cur_level -= 1;
}
cursor.add_child(node);
cur_level += 1;
}
}
1 post - 1 participant
🏷️ rust_feed