Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partial move of Vec of tuple

I have a Vec<(String, i64)> and need to iterate over the Strings and move them and then iterate over the i64s.

However, if I move the Strings I have to store the i64 again into another Vec:

let l: Vec<_> = l
    .into_iter()
    .map(|(string, int)| {
        drop(string);
        int
    })
    .collect();
                           
for i in l {
    process(i);
}

How can I iterate over the Strings and i64s separately without incurring any additional performance overhead.

The only solution I can think of at the moment that will not cause additional operations is to store the Strings and i64s separately.

like image 255
Nils André Avatar asked Dec 07 '21 13:12

Nils André


2 Answers

You can use std::mem::take() while iterating over the Vec in a first pass to take ownership of the String element while putting a non-allocating Default in its place. This allows you to keep the Vec in its original form, so no extra container is required.

fn foo(mut inp: Vec<(String, i64)>) {
    // First pass over the Vec "extracts" the owned Strings, replacing the content
    // in the Vec by a non-allocating empty String, which is close to zero cost;
    // this leaves the Vec as is, so no intermediate representation is needed.
    for s in inp.iter_mut().map(|(s, _)| std::mem::take(s)) {
        // Process String
    }

    // Something happens

    // Second pass ignores the empty strings, processes the integers
    for i in inp.into_iter().map(|(_, i)| i) {
        // Process the integers
    }
}
like image 154
user2722968 Avatar answered Oct 05 '22 12:10

user2722968


If the type of the list can be changed to Vec<Option<String>, i64> from Vec<String, i64>, then you can try the following way.

fn main() {
    let mut l = Vec::new();
    l.push((Some("a".to_string()), 1i64));
    l.push((Some("b".to_string()), 2));
    l.push((Some("c".to_string()), 3));
    l.push((Some("d".to_string()), 4));
    
    l.iter_mut().for_each(|(s, _)| {
        if let Some(x) = s.take() { 
            println!("Processing string: {}", x);
        }
    });

    l.iter().for_each(|(_, i)| {
        println!("Processing int: {}", i);
    });
}

Playground

like image 33
Joe_Jingyu Avatar answered Oct 05 '22 11:10

Joe_Jingyu