Cursor vs Position

⚓ rust    📅 2025-05-28    👤 surdeus    👁️ 4      

surdeus

Warning

This post was published 32 days ago. The information described in this article may have changed.

Info

This post is auto-generated from RSS feed The Rust Programming Language Forum - Latest topics. Source: Cursor vs Position

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

Read full topic

🏷️ rust_feed