shredder 0.2.0

Garbage collection as a library for Rust
Documentation
use shredder::marker::GcDrop;
use shredder::*;

trait Node: Scan + ToScan + GcDrop {
    fn max_number(&self) -> Option<i64>;
    fn longest_string(&self) -> Option<String>;
}

#[derive(Scan)]
struct TreeNode(Gc<dyn Node>, Gc<dyn Node>);

impl Node for TreeNode {
    fn max_number(&self) -> Option<i64> {
        let lhs = self.0.get().max_number();
        let rhs = self.1.get().max_number();
        match (lhs, rhs) {
            (Some(lhs), Some(rhs)) => Some(i64::max(lhs, rhs)),
            (value, None) | (None, value) if value.is_some() => value,
            _ => None,
        }
    }

    fn longest_string(&self) -> Option<String> {
        let lhs = self.0.get();
        let rhs = self.1.get();

        match (lhs.longest_string(), rhs.longest_string()) {
            (Some(lhs), Some(rhs)) => {
                if lhs.len() > rhs.len() {
                    Some(lhs)
                } else {
                    Some(rhs)
                }
            }
            (value, None) | (None, value) if value.is_some() => value,
            _ => None,
        }
    }
}

#[derive(Scan)]
struct NumberNode(i64);

impl Node for NumberNode {
    fn max_number(&self) -> Option<i64> {
        Some(self.0)
    }

    fn longest_string(&self) -> Option<String> {
        None
    }
}

#[derive(Scan)]
struct StringNode(String);

impl Node for StringNode {
    fn max_number(&self) -> Option<i64> {
        None
    }
    fn longest_string(&self) -> Option<String> {
        Some(self.0.clone())
    }
}

macro_rules! make_node {
    ($node:expr) => {{
        Gc::from_box(Box::new($node))
    }};
}

#[test]
fn from_box() {
    run_with_gc_cleanup(|| {
        assert_eq!(number_of_tracked_allocations(), 0);

        let num1: Gc<dyn Node> = make_node!(NumberNode(10));
        let num2: Gc<dyn Node> = make_node!(NumberNode(100));
        let num3: Gc<dyn Node> = make_node!(NumberNode(1000));
        let str1: Gc<dyn Node> = make_node!(StringNode("this is a string".to_string()));
        let str2: Gc<dyn Node> = make_node!(StringNode("this is a longer string".to_string()));
        let str3: Gc<dyn Node> = make_node!(StringNode("this is the longest string".to_string()));

        assert_eq!(number_of_tracked_allocations(), 6);

        {
            let str_root: Gc<dyn Node> = make_node!(TreeNode(str1.clone(), str2.clone()));
            let num_root: Gc<dyn Node> = make_node!(TreeNode(num1.clone(), num2.clone()));
            let root: Gc<dyn Node> = make_node!(TreeNode(str_root, num_root));

            assert_eq!(number_of_tracked_allocations(), 9);

            assert_eq!(root.get().max_number().unwrap(), 100);
            assert_eq!(
                root.get().longest_string().unwrap(),
                "this is a longer string"
            );
        }

        collect();
        assert_eq!(number_of_tracked_allocations(), 6);

        {
            let mixed_root1: Gc<dyn Node> = make_node!(TreeNode(str1.clone(), num1.clone()));
            let mixed_root2: Gc<dyn Node> = make_node!(TreeNode(str2.clone(), num2.clone()));
            let mixed_root3: Gc<dyn Node> = make_node!(TreeNode(str3.clone(), num3.clone()));

            let mid_root: Gc<dyn Node> = make_node!(TreeNode(mixed_root1, mixed_root2));
            let root: Gc<dyn Node> = make_node!(TreeNode(mixed_root3, mid_root));

            assert_eq!(number_of_tracked_allocations(), 11);

            assert_eq!(root.get().max_number().unwrap(), 1000);
            assert_eq!(
                root.get().longest_string().unwrap(),
                "this is the longest string"
            );
        }

        collect();
        assert_eq!(number_of_tracked_allocations(), 6);
    });
}