Implement IntoIterator for binary tree

喜你入骨 提交于 2019-12-02 12:24:10

The particular error that you are getting is that 'a should appear on the right of for. Otherwise, how could the compiler know what a is?

When implementing IntoIterator you have to decide whether the iterator will consume the container, or whether it'll just produce references into it. At the moment, your setup is inconsistent, and the error message points it out.

In the case of a binary tree, you also have to think about which order you want to produce the values in: traditional orders are depth first (yielding a sorted sequence) and breadth first (exposing the "layers" of the tree). I'll assume depth first as it's the most common one.


Let's tackle the case of a consuming iterator first. It's simpler in the sense that we don't have to worry about lifetimes.

#![feature(box_patterns)]

struct Node<T: PartialOrd> {
    value: T,
    left: Option<Box<Node<T>>>,
    right: Option<Box<Node<T>>>,
}

struct NodeIterator<T: PartialOrd> {
    stack: Vec<Node<T>>,
    next: Option<T>,
}

impl<T: PartialOrd> IntoIterator for Node<T> {
    type Item = T;
    type IntoIter = NodeIterator<T>;

    fn into_iter(self) -> Self::IntoIter {
        let mut stack = Vec::new();

        let smallest = pop_smallest(self, &mut stack);

        NodeIterator { stack: stack, next: Some(smallest) }
    }
}

impl<T: PartialOrd> Iterator for NodeIterator<T> {
    type Item = T;

    fn next(&mut self) -> Option<T> {
        if let Some(next) = self.next.take() {
            return Some(next);
        }

        if let Some(Node { value, right, .. }) = self.stack.pop() {
            if let Some(right) = right {
                let box right = right;
                self.stack.push(right);
            }
            return Some(value);
        }

        None
    }
}

fn pop_smallest<T: PartialOrd>(node: Node<T>, stack: &mut Vec<Node<T>>) -> T {
    let Node { value, left, right } = node;

    if let Some(left) = left {
        stack.push(Node { value: value, left: None, right: right });
        let box left = left;
        return pop_smallest(left, stack);
    }

    if let Some(right) = right {
        let box right = right;
        stack.push(right);
    }

    value
}

fn main() {
    let root = Node {
        value: 3,
        left: Some(Box::new(Node { value: 2, left: None, right: None })),
        right: Some(Box::new(Node { value: 4, left: None, right: None }))
    };

    for t in root {
        println!("{}", t);
    }
}

Now, we can "easily" adapt it to the non-consuming case by sprinkling in the appropriate references:

struct RefNodeIterator<'a, T: PartialOrd + 'a> {
    stack: Vec<&'a Node<T>>,
    next: Option<&'a T>,
}

impl<'a, T: PartialOrd + 'a> IntoIterator for &'a Node<T> {
    type Item = &'a T;
    type IntoIter = RefNodeIterator<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        let mut stack = Vec::new();

        let smallest = pop_smallest_ref(self, &mut stack);

        RefNodeIterator { stack: stack, next: Some(smallest) }
    }
}

impl<'a, T: PartialOrd + 'a> Iterator for RefNodeIterator<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<&'a T> {
        if let Some(next) = self.next.take() {
            return Some(next);
        }

        if let Some(node) = self.stack.pop() {
            if let Some(ref right) = node.right {
                self.stack.push(right);
            }
            return Some(&node.value);
        }

        None
    }
}

fn pop_smallest_ref<'a, T>(node: &'a Node<T>, stack: &mut Vec<&'a Node<T>>) -> &'a T
    where
        T: PartialOrd + 'a
{
    if let Some(ref left) = node.left {
        stack.push(node);
        return pop_smallest_ref(left, stack);
    }

    if let Some(ref right) = node.right {
        stack.push(right);
    }

    &node.value
}

There's a lot to unpack in there; so take your time to digest it. Specifically:

  • the use of ref in Some(ref right) = node.right is because I don't want to consume node.right, only to obtain a reference inside the Option; the compiler will complain that I cannot move out of a borrowed object without it (so I just follow the complaints),
  • in stack.push(right), right: &'a Box<Node<T>> and yet stack: Vec<&'a Node<T>>; this is the magic of Deref: Box<T> implements Deref<T> so the compiler automatically transforms the reference as appropriate.

Note: I didn't write this code as-is; instead I just put the first few references where I expect them to be (such as the return type of Iterator) and then let the compiler guide me.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!