I'm trying to use Iterator::fold()
with an accumulator that itself is an Iterator
, but in addition I need a clone of that iterator internally.
fn whatever<T,U,V,W>(some_iterator : T, other_cloneable_iterator_init : U) -> impl Iterator<Item=W>
where T: Iterator<Item=V>,
U: Iterator<Item=W>+Clone
{
some_iterator.fold(other_cloneable_iterator_init,|other_cloneable_iterator, value| {
let computed_value =
some_function_that_consumes_an_iterator(other_cloneable_iterator.clone(), value);
other_iterator.filter(|other_value| some_function(computed_value, other_value))
}
}
This of course does not work as written above, because the return type of the closure given to fold
is not the same as the type of the initializer.
They do however have in common that they both implement the Iterator<Item=W>+Clone
traits. This almost screams "erase the type by making it a trait object".
If it were just the Iterator<Item=W>
trait, I'd do just that. However, the Clone
trait is not object-safe. Searching for "clone boxed trait object" online yields various discussions, which all either require 'static
lifetime on the trait object (what Iterators
usually do not have), or using unsafe code (which I would like to avoid) like the dyn-clone crate.
So, if one wants to avoid using unsafe code, and does not have the luxury of getting 'static
lifetimes, how can one implement Clone
for a boxed trait object?
I'm talking about code like the following, which has lifetime issues and does not compile:
trait Cu32Tst<'a> : Iterator<Item=u32>+'a {
fn box_clone(&self) -> Box<dyn Cu32Tst+'a>;
}
impl<'a, T : Iterator<Item=u32>+Clone+'a> Cu32Tst<'a> for T {
fn box_clone(&self) -> Box<dyn Cu32Tst+'a> {
Box::new(self.clone())
}
}
impl<'a> Clone for Box<dyn Cu32Tst<'a>>{
fn clone(&self) -> Self {
self.box_clone()
}
}
The reason I want Clone
on the Box
itself is that in order to use it as accumulator type, I must be able to create a new Box<dyn IteratorTraitObject>
out of the output of the Iterator::filter()
method, which is only Clone
if the iterator it's called on is Clone
(I'd then have to implement Iterator
for the Box
as well then, but that could just forward to the contained value).
Long story short: Can Clone
be implemented for a trait object with finite lifetime, and if yes, how?
You need to modify the code to implement Box<dyn Cu32Tst<'a>>
rather than Box<dyn Cu32Tst + 'a>
. The latter presumably implements Cu32Tst<'static>
, which is not what you want for the blanket implementation of Clone
for Cu32Tst<'a>
. This compiles:
trait Cu32Tst<'a>: Iterator<Item = u32> + 'a {
fn box_clone(&self) -> Box<dyn Cu32Tst<'a>>;
}
impl<'a, T: Iterator<Item = u32> + Clone + 'a> Cu32Tst<'a> for T {
fn box_clone(&self) -> Box<dyn Cu32Tst<'a>> {
Box::new(self.clone())
}
}
impl<'a> Clone for Box<dyn Cu32Tst<'a>> {
fn clone(&self) -> Self {
self.as_ref().box_clone()
}
}
Note that the Clone
implementation invokes box_clone()
using self.as_ref().box_clone()
. This is because the blanket impl of Cu32Tst
matches against the Box<dyn Cu32Tst>
as it's both Clone
and Iterator
, so the box itself gets a box_clone()
method. As a result, self.box_clone()
would compile, but would cause infinite recursion.