A cute Rust micropattern

It's another of those nearly-daily "handy Rust micropatterns" that should feel light and natural. When you have code like this that essentially generates a matrix with rows of different lengths and then processes it:

fn generate() -> Vec<u32> {
    let mut result = vec![];
    // Push into result here...
    result
}

fn main() {
    let mut numbers = vec![];
    for _ in 0 .. 10 {
        numbers.push(generate());
    }

    // Use numbers here...
}

You could use this handy slicy pattern that reduces memory allocations (and could increase data locality a bit):

fn generate(all_numbers: &mut Vec<u32>) {
    // Push into all_numbers here...
}

fn main() {
    let mut all_numbers = vec![];
    let mut numbers = vec![];
    {
        let mut intervals = vec![];
        let mut len_old = 0;
        for _ in 0 .. 10 {
            generate(&mut all_numbers);
            intervals.push(all_numbers.len() - len_old);
            len_old = all_numbers.len();
        }

        let mut all_numbers_slice = &all_numbers[..];

        for i in 0 .. 9 {
            let (left, all_numbers_slice1) = all_numbers_slice.split_at(intervals[i]);
            all_numbers_slice = all_numbers_slice1;
            numbers.push(left);
        }
        numbers.push(all_numbers_slice);
    }

    // Use numbers here...
}

In my tests this in some cases gives a performance improvement. Probably there are ways to write this handy comfy happy pattern in a shorter/simpler way... :slight_smile:

This is basically building a compressed sparse row format (CSR) matrix without explicit column indices (since they are taken to be contiguous on each row starting from column 0).

It is indeed neat you can build a Vec of slices at the end. (and it looks like this should work even for mutable slices). Though you are stuck with a Vec<&[T]> or Vec<&mut[T]> afterwards (and can't freely switch between them), so I'd probably just wrap the flat Vec<T> and indices in a type that provides ways to access and iterate over rows

2 Likes