0

I have the following struct in Rust:

#[derive(Clone)]
pub struct EquationIterator<F> {
    counter: Box<dyn Iterator<Item = usize>>,
    formula: Arc<dyn Fn(usize) -> Equation<F>>,
}

I had to use an Arc to wrap the closure otherwise I couldn't implement Clone on the struct. Even with this, I still cannot derive Clone because the compiler is complaining about the dyn Iterator not implementing Clone.

I've tried implementing Clone manually but no luck either. Any idea?

To add more colors, this structure is an iterator that goes through some arbitrary range (e.g. counter = [2, 5, 9]) and calls some closure with the current counter value as argument

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9fb31441d9007d51c050bdd9e8f2d5c7

Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
David 天宇 Wong
  • 3,724
  • 4
  • 35
  • 47
  • Why aren't you using generics? – drewtato Jun 06 '23 at 00:21
  • in this case, I want to be able to have vectors of EquationIterators that don't have the same type of iterators. For example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=e1a51e067886abdfdd57b6a420f82855 – David 天宇 Wong Jun 06 '23 at 00:22
  • See this answer: https://stackoverflow.com/q/30353462/6274355 It's slightly different because you don't own the `Iterator` trait but the same restriction applies. – drewtato Jun 06 '23 at 00:52
  • One big problem you have is that `Clone` is not object safe. So you need your own version of `Clone` that returns a boxed trait object instead of `Self`. – Peter Hall Jun 06 '23 at 01:04
  • As a side note, you don't want to `derive` here because this adds the bound `F: Clone` which is not actually required (no `F` need be cloned). You will probably want to implement `Clone` manually. – cdhowie Jun 06 '23 at 02:44

2 Answers2

1

Because Clone isn't object-safe (it would return the actual type, not Box<dyn Clone>), you need a workaround. You can almost use the method from this question, except because you don't own the Iterator trait, you need to make a new trait. (playground)

trait ClonableIterator: Iterator {
    fn clone_box(&self) -> Box<dyn ClonableIterator<Item = Self::Item>>;
}

impl<T, I> ClonableIterator for T
where
    T: Iterator<Item = I> + Clone + 'static,
{
    fn clone_box(&self) -> Box<dyn ClonableIterator<Item = I>> {
        Box::new(self.clone())
    }
}

Then you can store a trait object of this trait in your struct.

counter: Box<dyn ClonableIterator<Item = usize>>

And you can implement Clone for your struct using clone_box.

impl<F> Clone for EquationIterator<F> {
    fn clone(&self) -> Self {
        EquationIterator {
            counter: self.counter.clone_box(),
            formula: self.formula.clone(),
        }
    }
}

You can also do this for the Fn trait, which I've upgraded to FnMut since it is now owned exclusively.

trait ClonableFnMut<T, O>: FnMut(T) -> O {
    fn clone_box(&self) -> Box<dyn ClonableFnMut<T, O>>;
}

impl<F, T, O> ClonableFnMut<T, O> for F
where
    F: FnMut(T) -> O + Clone + 'static,
{
    fn clone_box(&self) -> Box<dyn ClonableFnMut<T, O>> {
        Box::new(self.clone())
    }
}

And then you can add it to the struct.

formula: Box<dyn ClonableFnMut<usize, F>>

And change your Clone implementation.

formula: self.formula.clone_box()

I couldn't quite get this to work with dyn-clone, but there might be a way.

Note that you would need to implement Clone manually anyway because the derive would make it conditional on F being Clone, which isn't necessary.

drewtato
  • 6,783
  • 1
  • 12
  • 17
0

It is possible that an implementor of Iterator is not clonable. You need to specify to the compiler that only types that implement Clone and Interator are allowed.

use std::sync::Arc;

#[derive(Clone)]
pub struct EquationIterator<F, I: Iterator<Item = usize> + Clone> {
    counter: Box<I>,
    formula: Arc<dyn Fn(usize) -> F>,
}

fn main() {

}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9fb31441d9007d51c050bdd9e8f2d5c7

the_wizard
  • 477
  • 5
  • 9