Search code examples
rustiteratortraitstrait-objects

Can Clone be implemented for a trait object with finite lifetime (without using unsafe code)?


First things first: What am I trying to achieve?

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.

Why did this problem lead to this question?

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.

The actual question:

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?


Solution

  • 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()
        }
    }
    

    Playground

    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.