Double linked tree

Hello,
I'm new to rust and one of the first things I'm doing is extending the mdbook-latex program. The current approach is to use pulldown_cmark to directly transform md to latex code. Some problems arise like when a headline contains bold text. I thought it might be good to construct a (simple)syntax tree with children and parent pointers of each node. (Well in most other languages this would have been simple) however I struggled with rust which is why I'd like some opinions/styleguides/tipps etc.

The code here is only the Tree part to simplify things. The enum Value would get a lot more entries.

I think the structure is quite self explanatory the root of the tree is an Ast with possibly one Ast in the parent field and 0..many Ast in the children: Vec.

There is a rather complex impl Debug.

A trait with a function add_child to add one child (thereby updating the parent and children)

The main contains basically what usually is in testcases.

  • Does this code leak?
  • Is it written as you would write something in rust?
  • Are there better ways to write this(or in general a wrong idea)?
  • anything else you'd want to mention?

I thank you for any hint or even effort to guide me along to be a better rust programmer!

use std::cell::RefCell;
use std::rc::Weak;
use std::rc::Rc;

#[derive(Debug)]
pub enum Value {
    Text { content: String },
    Number { zahl: u32 },
}

pub struct Ast {
    node: Value,
    parent: Option<Weak<RefCell<Ast>>>,
    children: Vec<Rc<RefCell<Ast>>>,
}

impl std::fmt::Debug for Ast {
    fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
        match &self.node {
            Value::Text { content } => write!(formatter, "[{}, ", content)?,
            Value::Number { zahl } => write!(formatter, "[{}, ", zahl)?,
        };

        if &self.children.len() > &0 {
            let children: Vec<String> = self
                .children
                .iter()
                .map(|s| format!("{:?}", s.borrow()))
                .collect();
            write!(formatter, "[{}]", children.join(", "))?;
        } else {
            write!(formatter, ".")?;
        }
        write!(formatter, "]")
    }
}

pub trait AstModify {
    fn add_child(self: Self, node: Value) -> Self;
}

impl AstModify for Rc<RefCell<Ast>> {
    fn add_child(self: Self, node: Value) -> Self {
        let mut nc = Ast {
            node: node,
            parent: None,
            children: Vec::new(),
        };

        nc.parent = Some(Rc::downgrade(&Rc::clone(&self)));

        let rc = Rc::new(RefCell::new(nc));

        Rc::clone(&self).borrow_mut().children.push(Rc::clone(&rc));

        Rc::clone(&rc)
    }
}

fn main() {
    let mast = Rc::new(RefCell::new(Ast {
        node: Value::Number { zahl: 15 },
        parent: None,
        children: Vec::new(),
    }));

    println!("################\nrunning {:?}", mast.borrow());
    
    mast.clone().add_child(Value::Number { zahl: 16 });
    let c1 = mast.clone().add_child(Value::Number { zahl: 17 });
    
        c1.clone().add_child( Value::Text {
            content: "Test".to_string(),
        },
    );
    Rc::clone(&c1).add_child(
        Value::Text {
            content: "Test".to_string(),
        },
    );
    
        Rc::clone(&mast).add_child(        Value::Text {
            content: "Test".to_string(),
        },
    );
    println!("################\nafteradd {:?}", mast.borrow());
}

(Playground)

Output:

################
running [15, .]
################
afteradd [15, [[16, .], [17, [[Test, .], [Test, .]]], [Test, .]]]

Errors:

   Compiling playground v0.0.1 (/playground)
    Finished release [optimized] target(s) in 5.04s
     Running `target/release/playground`

mhm 80 views... and noone said anything... either it is good or it is so bad that people don't bother...

This code looks good overall. Your use of Weak is correct, and it means there are no memory leaks.

You could simplify the code a bit by changing add_child to take &self, like this:

fn add_child(&self, node: Value) -> Self {
    let nc = Ast {
        node,
        parent: Some(Rc::downgrade(self)),
        children: Vec::new(),
    };
    let rc = Rc::new(RefCell::new(nc));
    self.borrow_mut().children.push(Rc::clone(&rc));
    rc
}

Then you no longer need to use Rc::clone in main:

let c1 = mast.add_child(Value::Number { zahl: 17 });

c1.add_child(Value::Text {
    content: "Test".to_string(),
});
c1.add_child(Value::Text {
    content: "Test".to_string(),
});

Updated Playground

1 Like

Thank you a lot I appreciate your time and feedback it is very valuable to me! Now I can continue to code the full program without the fear to base it on some wonky code.

I'll adopt the simplification. And I just learned about the &self shortcut.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.