Performing a lazy copy


#1

This D code splits a string in parts lazily, converts each part in a u32 and assigns the numbers to the first items of a stack allocated array:

void main() {
    import std.stdio, std.conv, std.algorithm, std.string;
    uint[10] data;
    const txt = "1 2 3 4";
    txt.splitter.map!(to!uint).copy(data[]);
    data.writeln;
}

The output is:
[1, 2, 3, 4, 0, 0, 0, 0, 0, 0]

A similar Rust program:

fn main() {
    let mut data = [0u32; 10];
    let txt = "1 2 3 4";
    for (d, part) in data.iter_mut().zip(txt.split_whitespace()) {
        *d = part.parse().unwrap();
    }
    println!("{:?}", data);
}

Do I have something similar to copy() to copy a lazy iterable into something else, avoiding the explicit for loop?


#2

I am not aware of anything similar to it. But if you want to use it in many places, you may want to last-resort to writing your own.

pub trait LazyCopyIterator : Iterator {

    fn copy(self, data: &mut [Self::Item]) where Self: Sized {
        for (d, part) in data.iter_mut().zip(self) {
            *d = part;
        }
    }

}

impl<T: ?Sized> LazyCopyIterator for T where T: Iterator { }

fn main() {
    let mut data = [0u32; 10];
    let txt = "1 2 3 4";
    txt.split_whitespace().map(|part| part.parse().unwrap()).copy(&mut data);
    println!("{:?}", data);
}

#3

Thank you for the nice code. I use copy() now and then in D. So is this worth having in standard Rust?


#4

Not really, since it will be superseded by the possibility to use collect to collect into a fixed sized array. The previous attempt was denied on the grounds that integer generics and impl Trait syntax will both allow much better ways to implement it.


#5

PR32871 was to add array’s IntoIterator, but collect uses FromIterator. That’s potentially interesting for arrays too, although you’d have to assert that the source iterator provides enough items.


#6

In my example code, the source has less items than the destination fixed size array.


#7

Fewer items couldn’t be allowed for an array collect / FromIterator solution, because that has to produce the target collection as a return value, from scratch, and will need full initialization. Perhaps you could get away with it by using .chain(repeat(0)) on the source iterator to fill the remainder.

Then there’s also the question whether it should be an error to provide too many items. Arrays are a bit weird for FromIterator, compared to other collections that are dynamically sized.

FWIW ArrayVec decided it should not be an error to have extras in the iterator. And it isn’t bothered by having too few, since it has a length field. Maybe this will suit you better:

let a: ArrayVec<[u32; 10]> = iter.collect();