Cloning iterators behind dyn Boxes

I posted about this in the Rust discord, but I did not get any replies, so I thought posting here might be a better venue.

I have an iterator that I want to iterate over one of two things, and reuse its values multiple times.
This works, however I have to redo this after every usage:

let mut fov_iter: Box<dyn Iterator<Item = (u16, u16)>> = match full_vision {
    true => Box::new(grid_it_all.clone()),
    false => Box::new(grid_it_fov.clone()),
};

I could collect the iterator and in most use cases that is probably the best decision, and it's what I've changed my code to do but just for sake of example, e.g. where the full collect may be too large, I'd rather know how to clone the starting iterator behind the box.

I'd like to just directly clone the box and have that clone the underlying iterator, so I found the dyn-clone library and now I have

use dyn_clone::DynClone;

trait PosIterator: DynClone + Iterator<Item = (u16, u16)> {}
dyn_clone::clone_trait_object!(PosIterator);

#[derive(Clone)]
struct MyPosIterator {
    iter: Box<dyn PosIterator>,
}

But the iterator I'm creating is coming from a range with flat map, filter, etc applied, and does not implement PosIterator.

How do I do this?

In general, is it possible to implement additional traits on an iterator? or something like a 'newtrait pattern'? For example, augmenting the output of a range, or a flat map.

Iterator is a trait, not a type, which was leading me to a bigger question:

There may not be more than one non-auto trait [...]

Why can trait objects not implement more than one non-auto trait, when function signatures can have multiple trait bounds in an additive combination?

can you be more specific on why ?
FlatMap is Clone, Filter is Clone, all integer ranges are also Clone

edit : it seems like you forgot to write

impl<T : DynClone + Iterator<Item = (u16, u16)>> PosIterator for T {}

edit2: this compiles, so if yours doesn't i would really like to know why

use dyn_clone::DynClone;

trait PosIterator: DynClone + Iterator<Item = (u16, u16)> {}

impl<T: DynClone + Iterator<Item = (u16, u16)>> PosIterator for T {}
dyn_clone::clone_trait_object!(PosIterator);

#[derive(Clone)]
struct MyPosIterator {
    iter: Box<dyn PosIterator>,
}

fn x() -> MyPosIterator {
    MyPosIterator {
        iter: Box::new(
            (0..64u16)
                .filter(|&n| n.is_multiple_of(5) || n.is_multiple_of(7))
                .flat_map(|x| {
                    (1u16..).map_while(move |k| k.checked_mul(x).map(move |mul| (k, mul)))
                }),
        ),
    }
}

(oops I accidentally sent this while I was still writing)

Yes, my example is even using their Clone. The problem is the Box "doesn't know" that its member can be cloned.

So if I write something like

fov_iter.clone();

I get an error message like

error[E0599]: the method `clone` exists for struct `Box<dyn Iterator<Item = ecs::Pos>>`, but its trait bounds were not satisfied
   --> src/render_gamefield.rs:113:18
    |
113 |           fov_iter.clone();
    |                    ^^^^^

And because dyn takes only a single trait bound, I can't say it has an "Iterator + Clone".

In this situation to reduce the boilerplate, I could use a macro to write this longer "clone the iterator and recreate a Box with it", but this is only possible because I'm constructing and consuming the iterator in the same scope.

i feel to me like you are deliberately ignoring what you said in the original post.

you know you can't have dyn Iterator + Clone. that's what the whole PosIterator is for

Thank you for the example. (And sorry I accidentally sent the other post while I was still writing it, I didn't realise discourse would do that and I couldn't see an 'undo' button.)

So I was missing both impl<T: DynClone + Iterator<Item = Pos>> PosIterator for T {}, and the MyPosIterator { iter: Box::new(...)} wrapper. If I add this wrapper to what I have for my iterators which capture some variables, I get compiler errors about lifetimes which were easy to fix by adding explicit lifetimes to MyPosIterator and its captured references. Great!

Well yes, that's the second part of my question. It seems like a useful feature to have trait composition for dyn objects, with the same monomorphization that is done with trait bounds on function signatures, but over vtables instead. If I could do that here, I wouldn't need this kind of dyn clone pattern for this use case, and I don't think the combinatorial explosion in general for monomorphization should rule out this use case. I couldn't find any RFCs pointing to the reasoning behind this choice, for example. I figure there must be prior discussion of this decision somewhere though.

Supporting the former is pretty unrelated to the latter. The former is challenging as the number of combinations is exponential and there's no clear answer on how their vtables should work.

Thank you for the RFC link. I'll mark the thread as solved since my initial questions are, but it's certainly left more questions for me to read about.

So, "dyn upcasting coercion", but with "arbitrary combinations of traits", which is combined there to be called "arbitrary combination upcasting". Personally, this looks only additive to me, although there's apparently no concept of subtractive trait bounds in rust currently.

The monomorphization collector is run "just before MIR lowering and codegen", but the vtable construction does all combinations upfront? In the future, potentially pruning them at LTO, and "LTO currently executes at the LLVM level"? Well, I don't see why they can't both be done at adjacent steps in MIR, and have a similar monomorphization collector used for dyn objects.