Search code examples
rustiteratorclonerecursive-datastructuresdynamic-dispatch

Cloning a recursive dyn Iterator


For a project I'm working on (an implementation of an obscure programming language), I have an enum Data, which represents data the interpreter is keeping track of (e.g., a variable). This language has infinite lists/lazy arrays, so I'm using a dyn Iterator for those. Since these lists can be stored in Data, and they themselves contain Data, it's a recursive data structure.

This works fine, until I need to clone some Data, which happens quite often. Currently, Data looks something like this:

pub enum Data {
    Float(f64),
    Int(BigInt),
    String(String),
    Array(Box<dyn Iterator<Item = Data>>)
}

And if I try to #[derive(Clone)] for it:

error[E0277]: the trait bound `dyn Iterator<Item = Data>: Clone` is not satisfied
  --> src/shorispo.rs:28:11
   |
23 | #[derive(Clone)]
   |          ----- in this derive macro expansion
...
28 |     Array(Box<dyn Iterator<Item = Data>>)
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `dyn Iterator<Item = Data>`
   |
   = note: required because of the requirements on the impl of `Clone` for `Box<dyn Iterator<Item = Data>>`
   = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.

I've tried dyn-clone, but this seems to create a sort of chicken-and-egg problem, since Data would need to be Clone.

Is there a way I can do this?


Solution

  • You have to implement Clone manually for your type. And to Clone a trait object requires some special sauce, which I derived from this answer.

    // We have to provide a special trait for our clonable iterator,
    // since Clone requires a Sized type (so we can't call it on a trait object).
    trait CloneIterator: Iterator {
        fn clone_box(&self) -> Box<dyn CloneIterator<Item = Self::Item>>;
    }
    
    // Implement our special trait for all Cloneable Iterators
    impl<T> CloneIterator for T
    where
        T: 'static + Iterator + Clone,
    {
        fn clone_box(&self) -> Box<dyn CloneIterator<Item = Self::Item>> {
            Box::new(self.clone())
        }
    }
    
    // Use our special trait in Data instead
    pub enum Data {
        Float(f64),
        Int(i64),
        String(String),
        Array(Box<dyn CloneIterator<Item = Data>>),
    }
    
    // Implement Clone manually, making use of `clone_box` for our special trait
    impl Clone for Data {
        fn clone(&self) -> Self {
            match self {
                Self::Float(f) => Self::Float(f.clone()),
                Self::Int(i) => Self::Int(i.clone()),
                Self::String(s) => Self::String(s.clone()),
                Self::Array(b) => Self::Array(b.clone_box()),
            }
        }
    }
    

    playground