I have immutable vectors a
and b
where the elements are cheap to copy and I want to create a vector that forms the concatenation of these existing vectors without changing them (*).
An earlier question addressed it if one of the vectors is mutable, so an obvious answer is to first clone vector a
, e.g.
let mut r = a.clone();
r.extend(&b);
but that seems neither elegant nor efficient (extending can easily cause one needless reallocation, right?). The (corrected) best alternative I (being a Rust noob) come up with is:
fn cat(a: &Vec<i32>, b: &Vec<i32>) -> Vec<i32> {
let mut r = Vec::<i32>::with_capacity(a.len() + b.len());
r.extend(a);
r.extend(b);
r
}
Since elements are cheap to copy, the answer to the more generic question for vectors of strings should apply here, but vec![a, b].concat()
only seems to work if you construct the vector of vectors by moving the vectors into it, because vec![&a, &b].concat()
yields "no method named concat
found".
Is there a one-liner for this seemingly simple job, even if it's not optimal?
(*) turns out there are two meanings to "without changing":
concat
does work, if used correctly:
fn cat(a: &[i32], b: &[i32]) -> Vec<i32> {
[a, b].concat()
}
fn main() {
let a = vec![1, 2, 3];
let b = vec![7, 8, 9];
println!("{:?}", cat(&a, &b));
}
Arrays can concat()
slices to a vector, you just need to give a stronger hint to the compiler:
let combined = [a.as_slice(), b.as_slice()].concat();
Here's a rust playground to try it out. You can see that neither a
nor b
are consumed, and combined
is a new vector. Note that this fails if you try the &
shorthand for borrowing instead of specifying as_slice()
.
Editor's note: The OP changed their question after this answer was provided. Refer to the version of the question this answer was created from.
Your first example doesn't really make sense. You mention immutability, but since you are transferring ownership of the vectors to the cat
function, it can choose what the mutability is of the variables. In this case, you might as well just reuse the allocation of one of them:
fn cat(mut a: Vec<i32>, b: Vec<i32>) -> Vec<i32> {
a.extend(b);
a
}
extending can easily cause needless reallocation
This is technically possible, but extremely unlikely. There's a reason that iterators have the method size_hint
— this allows collections to allocate exactly whenever possible.
a one-liner
a.into_iter().chain(b).collect::<Vec<_>>()
This destroys the allocation of the vectors a
and b
(not of the elements inside them) and creates a new allocation to hold all the items.
If you had immutable slices, you can use the same technique:
fn cat<T: Clone>(a: &[T], b: &[T]) -> Vec<T> {
a.iter().chain(b).cloned().collect()
}
See also:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With