I would like to turn a vector into multiple disjoint subvectors such that the elements of the source vector v
are alternately put into the destination vectors x
and y
:
fn f<T>(v: Vec<T>) -> (Vec<T>, Vec<T>) {
let mut x = Vec::with_capacity((v.len() + 1) / 2);
let mut y = Vec::with_capacity(v.len() / 2);
let mut i = 0;
for e in v {
if i % 2 == 0 {
x.push(e);
} else {
y.push(e);
}
i += 1;
}
(x, y)
}
However, I would rather use a functional style:
use itertools::Itertools;
fn g<T>(v: Vec<T>) -> (Vec<T>, Vec<T>) {
v.into_iter().tuples().unzip()
}
(Yes, these two solutions don't produce the exact same results if the length of v is odd but that shouldn't matter here)
So what code does the compiler generate? As far as I can tell, it could pull some really neat tricks but it could also blindly allocate a temporary vector of tuples, copy the contents of v
into that temporary vector and then lastly allocate another two vectors that will hold the results.
What if I happen to write this?
fn h<T: Clone>(v: Vec<T>) -> (Vec<T>, Vec<T>) {
(
v.iter().step_by(2).cloned().collect(),
v.iter().skip(1).step_by(2).cloned().collect(),
)
}
Or even this?
fn i<T: Clone>(v: Vec<T>) -> (Vec<T>, Vec<T>) {
v.iter()
.step_by(2)
.cloned()
.zip(v.iter().skip(1).step_by(2).cloned())
.unzip()
}
Yes, the first two solutions are more generic (don't require T: Clone) but actually all of these functions describe the same computation. If the compiler was able to see that the two iterators operate on disjoint parts of v
, the calls to .cloned()
were unnecessary.
So how efficient functional Rust code tends to be in general?