Search code examples
rustownershipreference-counting

Rust: implementing an iterator from a vector of std::Rc smart pointers


I'm starting with Rust, and I'm already facing problems with data ownership.

I want to implement a generic struct called Port<T> that has a vector of values Vec<T>. Also, this struct has a vector of reference-counting pointers to other structs of the same type, Vec<Rc<Port<T>>>:

use std::slice::Iter;
use std::rc::Rc;

pub struct Port<T> {
    values: Vec<T>,
    ports: Vec<Rc<Port<T>>>,
}

The idea is the following: there are multiple structures of type Port<T>. You can add a value of type T to a given port. Each port stores these values in its values attribute. However, it is possible to "chain" one port to others using reference-counting pointers:

impl <T> Port<T> {
    pub fn new() -> Self {
        Self { values: vec![], ports: vec![] }
    }

    pub fn add_value(&mut self, value: T) {
        self.values.push(value);
    }

    pub fn chain_port(&mut self, port: Rc<Port<T>>) {
        if !port.is_empty() {
            self.ports.push(port)
        }
    }

    pub fn is_empty(&self) -> bool {
        self.values.is_empty() || self.ports.is_empty()
    }

    pub fn clear(&mut self) {
        self.values.clear();
        self.ports.clear();
    }
}

So far, the code compiles. Now, I want to implement an iterator for a port that returns references to the values owned by this port but also references to values generated by an iterator of every chained port:

pub struct PortIterator<'a, T> {
    values: Iter<'a, T>,                     // Iterates over values owned by Port<T>
    port: Option<Box<PortIterator<'a, T>>>,  // Pointer to current port iterator
    ports: Vec<Rc<Port<T>>>,                 // Pointers to remaining chained ports
}

// Note that the iterator is created from an immutable reference to Port<T>
impl<'a, T: 'a> IntoIterator for &'a Port<T> {
    type Item = &'a T;  // the iterator returns references to values
    type IntoIter = PortIterator<'a, T>;

    fn into_iter(self) -> Self::IntoIter {
        // We clone all the reference-counting pointers so the iterator owns them
        let mut ports = vec![];
        for port in self.ports.iter() {
            ports.push(port.clone())
        }
        PortIterator {values: self.values.iter(), port: None, ports}
    }
}

Now, let's define the Iterator trait for PortIterator:

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

    fn next(&mut self) -> Option<Self::Item> {
        // We first iterate over values of the original port
        if let Some(v) = self.values.next() {
            return Some(v)
        }
        // If the first iterable is done, we try to iterate over a chained port
        if let Some(port) = &mut self.port {
            if let Some(v) = port.next() {
                return Some(v)
            }
        }
        // if the chained port is over, we try to consume the next chained port
        if let Some(port) = self.ports.get(self.next_port) {
            self.next_port += 1;
            self.port = Some(Box::new(port.as_ref().into_iter()));
            return self.next()
        }
        None
    }
}

Now, the program does not compile. The issue seems to be in the third if let block, and it has to do with lifetimes. This is what the compiler says:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
  --> src/modeling/port.rs:69:40
   |
69 |         if let Some(port) = self.ports.get(self.next_port) {
   |                                        ^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 57:5...
  --> src/modeling/port.rs:57:5
   |
57 |     fn next(&mut self) -> Option<Self::Item> {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
  --> src/modeling/port.rs:69:29
   |
69 |         if let Some(port) = self.ports.get(self.next_port) {
   |                             ^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined on the impl at 54:7...
  --> src/modeling/port.rs:54:7
   |
54 | impl <'a, T: 'a> Iterator for PortIterator<'a, T> {
   |       ^^
note: ...so that the expression is assignable
  --> src/modeling/port.rs:71:25
   |
71 |             self.port = Some(Box::new(port.as_ref().into_iter()));
   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: expected `Option<Box<PortIterator<'a, _>>>`
              found `Option<Box<PortIterator<'_, _>>>`

I'm not sure how to deal with this. I've been trying other options and implementations, but I keep going in circles.


Solution

  • I think there are simpler ways of achieving what you want to achieve. Let's start small: your Port<T> need an iter(&self) method that returns an iterator that hands out &T items:

    pub fn iter(&self) -> impl Iterator<Item = &T> {
        // ...
    }
    

    This function needs to chain the iterator over self.values, i.e. self.values.iter() with an iterator over chained ports. What you'd want to write is something like:

    pub fn iter(&self) -> impl Iterator<Item = &T> {
        self.values
            .iter()
            .chain(self.ports.iter().flat_map(|p| p.iter()))
    }
    

    However, this doesn't compile because the compiler complains of a "recursive opaque type". That's because the type of p.iter() is our very same impl Iterator<...>, which would then have to contain itself. This is conceptually the same problem you encountered when building PortIterator, which you resolved by boxing the chained PortIterator. We can resolve it the same way, by boxing the inner iterator and dispatching it dynamically:

    pub fn iter(&self) -> impl Iterator<Item = &T> {
        self.values.iter().chain(
            self.ports
                .iter()
                .flat_map(|p| Box::new(p.iter()) as Box<dyn Iterator<Item = &T>>),
        )
    }