Iterate Vec<Box<dyn Trait>> with shadowing / at-most single mutable ownership

In implementing this I found a few bits of friction, but eventually worked things out to meet my intended use case. So, while this does what I need (and I thought it was interesting), I do also wonder if there might be other, further FP / immutable state / Haskell-esque implementation options I'm still missing, as that is a paradigm in which I'm still trying to improve my 'thinking'. But, really, any thoughts are welcome!

The last loops is simply expressed as a fold().

By the way, all of the closures are unnecessary (just name the trait method!), and so is the last loop explicitly dropping items. When a vector/box/other collection is dropped, it frees its elements. Ranges are already iterators, so (0..3).into_iter() is also unnecessary.

The simplified code (Playground):

fn main() {
    let for_each = (0..3)
        .map(|i| Box::new(A(i)))
        .collect::<Vec<_>>();
        
    println!("Initial state:\n  {:?}", for_each);
        
    let for_each = for_each.into_iter()
        .map(<_>::into_next)
        .collect::<Vec<_>>();
        
    println!("\nIterated once, now state is:\n  {:?}", for_each);
    
    let for_each = (0..10).fold(for_each, |state, _i| {
        state.into_iter()
            .map(<_>::into_next)
            .collect()
    });
    
    println!("\nIterated in a loop, now state is:\n  {:?}", for_each);
}
2 Likes

Thanks, those are some good notes. Getting rid of the closures is particularly nice. And now I see that's how the drop line was already working (it was just in there to clear the compiler warning, before I had it print to actually used the values).

So, now I've got a question on the fold as loop replacement. I've switched to fold_while() from itertools so I could use my own exit conditions, but it still feels a bit rough. Is this the cleanest I'll be able to make this [playground]?

let x: f64 = 0.0;

// fold_while does what I want, but requires matching on exit to parse:
let for_each = match (0..)
    .fold_while((for_each, x), |(state, x), _i| {
        if x >= 10.0 {
            FoldWhile::Done((state, x))
        } else {
            let x = x + 0.1;
            
            FoldWhile::Continue((state.into_iter()
                .map(BoxedDynInto::into_next)
                .collect::<Vec<_>>(), x))
        }
    }) {
        FoldWhile::Done((state, _x)) => state,
        _ => panic!(),
    };

Why don't you just use the into_inner() method?

Apart from not matching, you could clean the code up syntactically by removing/shuffling around some type annotations and by introducing some additional bindings. Playground:

fn main() {
    let for_each: Vec<_> = (0..3).map(|i| Box::new(A(i))).collect();
    println!("Initial state:\n  {:?}", for_each);
    
    let for_each: Vec<_> = for_each.into_iter().map(<_>::into_next).collect();
    println!("\nIterated once, now state is:\n  {:?}", for_each);

    let x: f64 = 0.0;
    let (for_each, _) = (0..).fold_while((for_each, x), |(state, x), _i| {
        if x >= 10.0 {
            FoldWhile::Done((state, x))
        } else {
            let state = state.into_iter().map(<_>::into_next).collect();
            let x = x + 0.1;
            FoldWhile::Continue((state, x))
        }
    }).into_inner();
    
    println!("\nIterated in a loop, now state is:\n  {:?}", for_each);
}

Ah, that's what I was missing. Thanks again! I was getting stuck trying:

let FoldWhile::Done((for_each, _)) = (0..).fold_while(/* do stuff */)

Apparently the answer was to just forge ahead and find that rayon already has collect_into_vec as a recommended option, unless I'm misunderstanding their claim that it "reuses the same backing buffer."