The following snippet is a simple function for pretty printing trees and other hierarchical data, such as HTML and ASTs.

There's also a version written in Python.

Output Example

`- Root
   |- Node 1
   |  |- Node 1.1
   |  |  `- Node 1.1.1
   |  |     |- Node 1.1.1.1
   |  |     `- Node 1.1.1.2
   |  |- Node 1.2
   |  |- Node 1.3
   |  |  `- Node 1.3.1
   |  `- Node 1.4
   |     |- Node 1.4.1
   |     `- Node 1.4.2
   |        |- Node 1.4.2.1
   |        `- Node 1.4.2.2
   |           `- Node 1.4.2.2.1
   |- Node 2
   |  |- Node 2.1
   |  `- Node 2.2
   `- Node 3

Snippet

The pprint_tree function is implemented using an inner function as a way to hide temporary parameters from the user. The pprint_tree function requires something that implements std::fmt::Display as well as providing a children field of type Vec<Node>. In the complete example, Node is implemented as a struct, but could be implemented as a trait.

fn pprint_tree(node: &Node) {
    fn pprint_tree(node: &Node, prefix: String, last: bool) {
        let prefix_current = if last { "`- " } else { "|- " };

        println!("{}{}{}", prefix, prefix_current, node);

        let prefix_child = if last { "   " } else { "|  " };
        let prefix = prefix + prefix_child;

        if !node.children.is_empty() {
            let last_child = node.children.len() - 1;

            for (i, child) in node.children.iter().enumerate() {
                pprint_tree(&child, prefix.to_string(), i == last_child);
            }
        }
    }

    pprint_tree(node, "".to_string(), true);
}

Complete Example

use std::fmt;

struct Node {
    name: &'static str,
    children: Vec<Node>,
}

impl Node {
    fn new(name: &'static str, children: Vec<Node>) -> Node {
        Node { name, children }
    }
}

impl fmt::Display for Node {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.name)
    }
}

fn pprint_tree(node: &Node) {
    fn pprint_tree(node: &Node, prefix: String, last: bool) {
        let prefix_current = if last { "`- " } else { "|- " };

        println!("{}{}{}", prefix, prefix_current, node);

        let prefix_child = if last { "   " } else { "|  " };
        let prefix = prefix + prefix_child;

        if !node.children.is_empty() {
            let last_child = node.children.len() - 1;

            for (i, child) in node.children.iter().enumerate() {
                pprint_tree(&child, prefix.to_string(), i == last_child);
            }
        }
    }

    pprint_tree(node, "".to_string(), true);
}

fn main() {
    let tree = Node::new("Root", vec![
        Node::new("Node 1", vec![
            Node::new("Node 1.1", vec![
                Node::new("Node 1.1.1", vec![
                    Node::new("Node 1.1.1.1", vec![]),
                    Node::new("Node 1.1.1.2", vec![]),
                ]),
            ]),
            Node::new("Node 1.2", vec![]),
            Node::new("Node 1.3", vec![
                Node::new("Node 1.3.1", vec![]),
            ]),
            Node::new("Node 1.4", vec![
                Node::new("Node 1.4.1", vec![]),
                Node::new("Node 1.4.2", vec![
                    Node::new("Node 1.4.2.1", vec![]),
                    Node::new("Node 1.4.2.2", vec![
                        Node::new("Node 1.4.2.2.1", vec![]),
                    ]),
                ]),
            ]),
        ]),
        Node::new("Node 2", vec![
            Node::new("Node 2.1", vec![]),
            Node::new("Node 2.2", vec![]),
        ]),
        Node::new("Node 3", vec![]),
    ]);

    pprint_tree(&tree);
}